This is my response to the Perl Weekly Challenge #58.
Compare two given version number strings v1 and v2 such that:
The version numbers are non-empty strings containing only digits, and the dot (“.”) and underscore (“_”) characters. (“_” denotes an alpha/development version, and has a lower precedence than a dot, “.”). Here are some examples:
Version numbers may also contain leading zeros. You may handle these how you wish, as long as it’s consistent. |
This is easy, as Raku has a built-in Version
class:
unit sub MAIN (Str $v1, Str $v2);
say Version.new($v1) <=> Version.new($v2); # [1]
[1]
Using the generic cmp
instead of the
numeric <=>
gives the same result.
Running it:
$ raku comp-ver 0.1 1.1 # -> Less
See
docs.raku.org/type/Version
for more information about the Version
class.
See
docs.raku.org/routine/<=>
for more information about the Numeric three-way comparator <=>
.
See
docs.raku.org/routine/cmp
for more information about the Generic, "smart" three-way comparator
cmp
.
The <=>
and cmp
operators gives one of «Less»,
«Same» or «More» as result. That isn't what the challenge asked for, but
we can coerce the result to an integer, and this will give us one of «-1»,
«0» and «1».
unit sub MAIN (Str $v1, Str $v2);
say (Version.new($v1) cmp Version.new($v2)).Int;
Running it:
$ raku comp-ver-int 0.1 1.1 # -> -1
$ raku comp-ver-int 2.0 1.2 # -> 1
$ raku comp-ver-int 1.2 1.2_5 # -> 1 (Expected «-1»)
$ raku comp-ver-int 1.2.1 1.2_1 # -> 1
$ raku comp-ver-int 1.2.1 1.2.1 # -> 0
Raku is in agreement with the challenge for 4 of the 5 expressions.
The values «Less», «Same» and «More» are an enum
. Let us try it out in REPL:
> my $a = 1 <=> 3; say $a; say $a.WHAT;
Less
(Order)
We can use the enums
method to get the values for an
Enum(erated) type:
> say Order.enums;
Map.new((Less => -1, More => 1, Same => 0))
See
docs.raku.org/language/typesystem#enum
for more information about the enum
type.
See
docs.raku.org/routine/enums
for more information about the enums
method.
Map
is essentially a
read only version of a hash. See
docs.raku.org/type/Map for more
information about the Map
class.
We can try some other languages, and see what they report.
Perl has a «version» module that we can use, so the code is similar:
File: comp-ver.perl
use version;
use feature say;
say version->parse( $ARGV[0] ) <=> version->parse( $ARGV[1] );
Here is a version in php, which has a built-in function to compare version strings:
File: comp-ver.php
<?php
echo version_compare($argv[1], $argv[2]) . "\n"; # [1]
?>
[1] $argv[0]
gives the program name, so don't use that.
Ruby has a built-in class, so we get a program that looks very much like the Raku one:
File: comp-ver.ruby
puts Gem::Version.new(ARGV[0]) <=> Gem::Version.new(ARGV[1])
Running it with a version string with an underscore causes an exception, and a lot of noise as output. We should catch that error to make the output nicer:
File: comp-ver.ruby (modified version)
begin
puts Gem::Version.new(ARGV[0]) <=> Gem::Version.new(ARGV[1]);
rescue
puts "ERROR";
end
Python doesn't have a three way comparison operator, so we have to write some more code:
File: comp-ver.pyfrom packaging import version
import sys
v1 = version.parse(sys.argv[1])
v2 = version.parse(sys.argv[2])
if v1 > v2:
print "1"
elif v1 < v2:
print "-1"
else:
print "0"
I didn't show the result of manually running the programs with the sample data from the challenge, as that would have required a lot of typing on my part. Here is a script (in Bash) that does it for us:
File:comp-ver.sh
function comp-ver
{
echo "$1 cmp $2 -> $3";
echo -n "Raku /1: "
./comp-ver $1 $2
echo -n "Raku /2: "
./comp-ver-int $1 $2
echo -n "Perl: "
./comp-ver.perl $1 $2
echo -n "PHP: "
./comp-ver.php $1 $2
echo -n "Ruby: "
./comp-ver.ruby $1 $2
echo -n "Python: "
./comp-ver.py $1 $2
echo "";
}
comp-ver 0.1 1.1 -1;
comp-ver 2.0 1.2 1;
comp-ver 1.2 1.2_5 -1;
comp-ver 1.2.1 1.2_1 1;
comp-ver 1.2.1 1.2.1 0;
The output gives the two version string to compare, and the expected result. Then the actual result from the programs, prefixed with the language. Then a blank line, before the next comparison.
Running it:
$ bash comp-ver.sh
0.1 cmp 1.1 -> -1
Raku /1: Less
Raku /2: -1
Perl: -1
PHP: -1
Ruby: -1
Python: -1
2.0 cmp 1.2 -> 1
Raku /1: More
Raku /2: 1
Perl: 1
PHP: 1
Ruby: 1
Python: 1
1.2 cmp 1.2_5 -> -1
Raku /1: More
Raku /2: 1
Perl: -1
PHP: -1
Ruby: ERROR
Python: 1
1.2.1 cmp 1.2_1 -> 1
Raku /1: More
Raku /2: 1
Perl: -1
PHP: 0
Ruby: ERROR
Python: 1
1.2.1 cmp 1.2.1 -> 0
Raku /1: Same
Raku /2: 0
Perl: 0
PHP: 0
Ruby: 0
Python: 0
All the languages got it right where the version strings didn't have underscores. Ruby don't support underscores at all (and go up in flames), Raku and Python got the first underscore comparison wrong, but the second one right. Perl and PHP got the first underscore comparison right, but the second one wrong. Also note that the answers are all over the place for the fourth example.
Conclusion: Version strings are difficult. Note that I didn' actually answer the challenge, as none of the programs I have supllied get all the examples right.
Write a script to arrange people in a lineup according to how many taller people are in front of each person in line. You are given two arrays. @H is a list of unique heights, in any order. @T is a list of how many taller people are to be put in front of the corresponding person in @H. The output is the final ordering of people’s heights, or an error if there is no solution. Here is a small example:
The ordering of both arrays lines up, so Here is a diagram of the input arrays @H and @T:
Finally, here is one possible solution that satisfies @H and @T:
As per the last diagram, your script would then output the ordering
Here’s a 64-person example, with answer provided:
You’re free to come up with your own inputs. Here is a 1000-person list, if you like! |
There should be only one solution to this, if all the heights are different. On the other hand, for two persons to have the same heights they would have to have the same number of taller persons in front of them. That makes them exactly the same (the two values that is; they can be different persons all the same), and it is impossible to separate them.
Let us start with that non-duplicate assumption, and see how it pans out.
File: ordered-lineup (partial)
unit sub MAIN (:$medium, :$verbose); # [1]
my @H = (2, 6, 4, 5, 1, 3);
my @T = (1, 0, 2, 0, 1, 2);
if $medium
{
@H = (27, 21, 37, 4, 19, 52, 23, 64, 1, 7, 51, 17, 24, 50, 3, 2,
34, 40, 47, 20, 8, 56, 14, 16, 42, 38, 62, 53, 31, 41, 55, 59,
48, 12, 32, 61, 9, 60, 46, 26, 58, 25, 15, 36, 11, 44, 63, 28,
5, 54, 10, 49, 57, 30, 29, 22, 35, 39, 45, 43, 18, 6, 13, 33);
@T = ( 6, 41, 1, 49, 38, 12, 1, 0, 58, 47, 4, 17, 26, 1, 61, 12,
29, 3, 4, 11, 45, 1, 32, 5, 9, 19, 1, 4, 28, 12, 2, 2,
13, 18, 19, 3, 4, 1, 10, 16, 4, 3, 29, 5, 49, 1, 1, 24,
2, 1, 38, 7, 7, 14, 35, 25, 0, 5, 4, 19, 10, 13, 4, 12);
}
die "Repeated heights" if @H.repeated; # [2]
[1] Specify «--medium» to get the longer sample.
[2] repeated
gives us any duplicates in the list.
In Boolean context it gives True
if there are duplicates.
See
docs.raku.org/routine/repeated
for more information about the repeated
function.
Whatever approach we should choose, we have to sort the persons. Either by height or the number of taller people in front. We can do this with an array, but it is easier to use objects and sort a list of them. (The array version should be faster, but the difference doesn't matter here.)
File: ordered-lineup (partial)
class Lineup # [3]
{
has Int $.height; # [3a]
has Int $.taller; # [3b]
}
my @list; # [4]
for ^@H -> $index # [5]
{
@list.push: Lineup.new(height => @H[$index], taller => @T[$index]);
}
[3] A «Lineup» class, with two variables: the height [3a] and the number of taller people in front [3b].
[4] The list of «Lineup» objects, in the same order as given in the arrays.
[5] Create the objects and add them to «@list», one at a time.
Then the sorting. The height looks promising, if we do them in descending height order. Then all the persons already placed are higher than the current one, and it is easy to place the current one.
The idea is to start with the sorted persons (with the higest first), and add them to the «@result» array, somewhere:
File: ordered-lineup (partial)
my @result;
for @list.sort({ $^b.height <=> $^a.height }) -> $elem # [6]
{
...
}
[6] I haven't bothered writing a method that returns the height when we use a «Lineup» object in numeric context, as it is easy to look up the height attribute inside the sort code. Note the use of placeholder variables in reverse order, so that the objects are sorted the wrong way (with the highest person first).
Let us walk it through first, and write the code afterwards.
We start with the highest person (H:6) from the «Sorted» illustration above. The «@result» array is empty, so there is only one place for the person. Note that the number of taller persons in «@result» is 0, as it has to be:
The next person (H:5) is lower than the one we have placed in «@result». This one also has no taller persons in front of it, so it must be placed before the one already in «@result» (H:6):
This person (H:4) has 2 taller persons in front (requiring a list of two persons, which we have), so is placed at the end:
This one (H:3) also has 2 taller persons in front, so must be inserted to the left of the person with height 4 (pushing the rest of list to the right):
This one (H:2) has 1 taller person in front of it, so must be placed after the first one (H:5):
The last person (H:1) also has 1 taller person in front of it, so must be placed after the first one (H:5), pushing H:2 to the right:
That worked out, and we got the correct answer.
The code looks like this:
File: ordered-lineup (partial)
my @result;
for @list.sort({ $^b.height <=> $^a.height }) -> $elem
{
my $taller = $elem.taller; # [7]
say ": H:{ $elem.height } -> T:{ $elem.taller }" if $verbose;
if (@result[$taller].defined) # [8]
{
@result.splice($taller, 1, $elem, @result[$taller]); # [8a]
}
else
{
die "Not enough taller persons in front of { $elem.height }: \
{ @result.elems } (should have been $taller)"
if @result.elems != $taller; # [9]
@result.push: $elem; # [8b]
}
}
say ": " ~ @result.raku if $verbose;
say @result>>.height.join(", "); # [10]
[7] The number of taller persons in front of the current one.
[8] if there already is a person at that position, move her (and the rest of the array) one position to the right and insert the person there [8a]. If not, just add the person to the end.
[9] Check that [8b] actually is the end. (If e.g. the person has 2 taller persons, and there are only 1 in the list, we have an error.
[10] Get the heights from the objects, and print them as a comma separated list.
Running it (with newlines added to make it fit the article with):
$ raku ordered-lineup
5, 1, 2, 6, 3, 4
$ raku ordered-lineup --verbose
: H:6 -> T:0
: H:5 -> T:0
: H:4 -> T:2
: H:3 -> T:2
: H:2 -> T:1
: H:1 -> T:1
: [Lineup.new(height => 5, taller => 0), Lineup.new(height => 1, taller => 1),
Lineup.new(height => 2, taller => 1), Lineup.new(height => 6, taller => 0),
Lineup.new(height => 3, taller => 2), Lineup.new(height => 4, taller => 2)]
5, 1, 2, 6, 3, 4
Then the longer sample (with even more newlines added):
$ raku /ordered-lineup --medium
35, 23, 5, 64, 37, 9, 13, 25, 16, 44, 50, 40, 2, 27, 36, 6, 18, 54, 20, 39, \
56, 45, 12, 47, 17, 33, 55, 30, 26, 51, 42, 53, 49, 41, 32, 15, 22, 60, 14, \
46, 24, 59, 10, 28, 62, 38, 58, 63, 8, 48, 4, 7, 31, 19, 61, 43, 57, 11, 1, \
34, 21, 52, 29, 3
$ raku ordered-lineup --medium --verbose
: H:64 -> T:0
: H:63 -> T:1
: H:62 -> T:1
: H:61 -> T:3
: H:60 -> T:1
: H:59 -> T:2
: H:58 -> T:4
: H:57 -> T:7
: H:56 -> T:1
: H:55 -> T:2
: H:54 -> T:1
: H:53 -> T:4
: H:52 -> T:12
: H:51 -> T:4
: H:50 -> T:1
: H:49 -> T:7
: H:48 -> T:13
: H:47 -> T:4
: H:46 -> T:10
: H:45 -> T:4
: H:44 -> T:1
: H:43 -> T:19
: H:42 -> T:9
: H:41 -> T:12
: H:40 -> T:3
: H:39 -> T:5
: H:38 -> T:19
: H:37 -> T:1
: H:36 -> T:5
: H:35 -> T:0
: H:34 -> T:29
: H:33 -> T:12
: H:32 -> T:19
: H:31 -> T:28
: H:30 -> T:14
: H:29 -> T:35
: H:28 -> T:24
: H:27 -> T:6
: H:26 -> T:16
: H:25 -> T:3
: H:24 -> T:26
: H:23 -> T:1
: H:22 -> T:25
: H:21 -> T:41
: H:20 -> T:11
: H:19 -> T:38
: H:18 -> T:10
: H:17 -> T:17
: H:16 -> T:5
: H:15 -> T:29
: H:14 -> T:32
: H:13 -> T:4
: H:12 -> T:18
: H:11 -> T:49
: H:10 -> T:38
: H:9 -> T:4
: H:8 -> T:45
: H:7 -> T:47
: H:6 -> T:13
: H:5 -> T:2
: H:4 -> T:49
: H:3 -> T:61
: H:2 -> T:12
: H:1 -> T:58
: [
Lineup.new(height => 35, taller => 0), Lineup.new(height => 23, taller => 1),
Lineup.new(height => 5, taller => 2), Lineup.new(height => 64, taller => 0),
Lineup.new(height => 37, taller => 1), Lineup.new(height => 9, taller => 4),
Lineup.new(height => 13, taller => 4), Lineup.new(height => 25, taller => 3),
Lineup.new(height => 16, taller => 5), Lineup.new(height => 44, taller => 1),
Lineup.new(height => 50, taller => 1), Lineup.new(height => 40, taller => 3),
Lineup.new(height => 2, taller => 12), Lineup.new(height => 27, taller => 6),
Lineup.new(height => 36, taller => 5), Lineup.new(height => 6, taller => 13),
Lineup.new(height => 18, taller => 10), Lineup.new(height => 54, taller => 1),
Lineup.new(height => 20, taller => 11), Lineup.new(height => 39, taller => 5),
Lineup.new(height => 56, taller => 1), Lineup.new(height => 45, taller => 4),
Lineup.new(height => 12, taller => 18), Lineup.new(height => 47, taller => 4),
Lineup.new(height => 17, taller => 17), Lineup.new(height => 33, taller => 12),
Lineup.new(height => 55, taller => 2), Lineup.new(height => 30, taller => 14),
Lineup.new(height => 26, taller => 16), Lineup.new(height => 51, taller => 4),
Lineup.new(height => 42, taller => 9), Lineup.new(height => 53, taller => 4),
Lineup.new(height => 49, taller => 7), Lineup.new(height => 41, taller => 12),
Lineup.new(height => 32, taller => 19), Lineup.new(height => 15, taller => 29),
Lineup.new(height => 22, taller => 25), Lineup.new(height => 60, taller => 1),
Lineup.new(height => 14, taller => 32), Lineup.new(height => 46, taller => 10),
Lineup.new(height => 24, taller => 26), Lineup.new(height => 59, taller => 2),
Lineup.new(height => 10, taller => 38), Lineup.new(height => 28, taller => 24),
Lineup.new(height => 62, taller => 1), Lineup.new(height => 38, taller => 19),
Lineup.new(height => 58, taller => 4), Lineup.new(height => 63, taller => 1),
Lineup.new(height => 8, taller => 45), Lineup.new(height => 48, taller => 13),
Lineup.new(height => 4, taller => 49), Lineup.new(height => 7, taller => 47),
Lineup.new(height => 31, taller => 28), Lineup.new(height => 19, taller => 38),
Lineup.new(height => 61, taller => 3), Lineup.new(height => 43, taller => 19),
Lineup.new(height => 57, taller => 7), Lineup.new(height => 11, taller => 49),
Lineup.new(height => 1, taller => 58), Lineup.new(height => 34, taller => 29),
Lineup.new(height => 21, taller => 41), Lineup.new(height => 52, taller => 12),
Lineup.new(height => 29, taller => 35), Lineup.new(height => 3, taller => 61)]
35, 23, 5, 64, 37, 9, 13, 25, 16, 44, 50, 40, 2, 27, 36, 6, 18, 54, 20, 39, \
56, 45, 12, 47, 17, 33, 55, 30, 26, 51, 42, 53, 49, 41, 32, 15, 22, 60, 14, \
46, 24, 59, 10, 28, 62, 38, 58, 63, 8, 48, 4, 7, 31, 19, 61, 43, 57, 11, 1, \
34, 21, 52, 29, 3
It isn't immidiately obvious that the result is correct, but we can let the program check it for us:
File: ordered-lineup-check (changes only)
unit sub MAIN (:$medium, :$verbose , :$compare);
my @H = (2, 6, 4, 5, 1, 3);
my @T = (1, 0, 2, 0, 1, 2);
my @A = (5, 1, 2, 6, 3, 4);
if $medium
{
@H = (27, 21, 37, 4, 19, 52, 23, 64, 1, 7, 51, 17, 24, 50, 3, 2,
34, 40, 47, 20, 8, 56, 14, 16, 42, 38, 62, 53, 31, 41, 55, 59,
48, 12, 32, 61, 9, 60, 46, 26, 58, 25, 15, 36, 11, 44, 63, 28,
5, 54, 10, 49, 57, 30, 29, 22, 35, 39, 45, 43, 18, 6, 13, 33);
@T = ( 6, 41, 1, 49, 38, 12, 1, 0, 58, 47, 4, 17, 26, 1, 61, 12,
29, 3, 4, 11, 45, 1, 32, 5, 9, 19, 1, 4, 28, 12, 2, 2,
13, 18, 19, 3, 4, 1, 10, 16, 4, 3, 29, 5, 49, 1, 1, 24,
2, 1, 38, 7, 7, 14, 35, 25, 0, 5, 4, 19, 10, 13, 4, 12);
@A = (35, 23, 5, 64, 37, 9, 13, 25, 16, 44, 50, 40, 2, 27, 36, 6,
18, 54, 20, 39, 56, 45, 12, 47, 17, 33, 55, 30, 26, 51, 42, 53,
49, 41, 32, 15, 22, 60, 14, 46, 24, 59, 10, 28, 62, 38, 58, 63,
8, 48, 4, 7, 31, 19, 61, 43, 57, 11, 1, 34, 21, 52, 29, 3);
}
...
say @result>>.height.join(", ");
say @result>>.height eqv @A if $compare; # [1]
[1] Get the heights of the elements (as a list),
and compare with the known solution. The Equivalence Operator eqv
checks
that the two arrays are identical (in length, and with the same values).
See
docs.raku.org/routine/eqv for more
information about the Equivalence Operator eqv
.
Running it:
$ raku ordered-lineup-check --compare
5, 1, 2, 6, 3, 4
True
$ raku ordered-lineup-check --compare --medium
35, 23, 5, 64, 37, 9, 13, 25, 16, 44, 50, 40, 2, 27, 36, 6, 18, 54, 20, 39, \
56, 45, 12, 47, 17, 33, 55, 30, 26, 51, 42, 53, 49, 41, 32, 15, 22, 60, 14, \
46, 24, 59, 10, 28, 62, 38, 58, 63, 8, 48, 4, 7, 31, 19, 61, 43, 57, 11, 1, \
34, 21, 52, 29, 3
True
And yes, that final «True» tells us that we have the correct answer.
We can do the list with 1000 items as well, but it does not come with an answer so «--check» must be ignored.
File: ordered-lineup-1000 (changes only)
unit sub MAIN (:$medium, :$large, :$verbose, :$compare is copy); # [1]
if $large
{
@H = ...;
@T = ...;
$compare = False; # [1]
}
elsif $medium
{
...
}
[1] Procedure arguments are read only by default. is copy
gives a local copy that we can change.
See
docs.raku.org/type/Signature#index-entry-trait_is_copy
for more information about the is copy
trait.
Running it works (i.e. no duplicates):
$ raku ordered-lineup-1000 --large
204, 926, 704, 543, 760, 691, 795, 303, 136, 650, 749, 721, 388, 70, 461, \
429, 585, 276, 819, 523, 160, 92, 134, 40, 46, 440, 989, 394, 374, 566, \
842, 851, 516, 898, 448, 283, 54, 975, 813, 123, 342, 142, 797, 613, 163, \
982, 392, 529, 300, 240, 953, 491, 519, 225, 945, 53, 872, 197, 209, 941, \
298, 884, 955, 48, 930, 788, 967, 494, 664, 130, 120, 294, 467, 950, 94, \
513, 416, 398, 707, 355, 890, 68, 559, 869, 640, 266, 183, 158, 711, 179, \
301, 616, 49, 861, 633, 826, 159, 684, 475, 460, 151, 498, 998, 680, 147, \
540, 496, 720, 807, 412, 595, 393, 153, 860, 521, 341, 369, 97, 852, 539, \
700, 712, 318, 140, 546, 446, 895, 669, 246, 268, 241, 569, 897, 269, 112, \
833, 786, 960, 905, 293, 772, 682, 331, 894, 117, 676, 62, 940, 156, 256, \
690, 933, 641, 843, 29, 380, 199, 202, 577, 714, 634, 274, 185, 203, 570, \
133, 571, 173, 964, 497, 128, 538, 423, 172, 162, 947, 722, 295, 827, 19, \
436, 345, 552, 723, 822, 477, 109, 695, 790, 150, 474, 330, 488, 929, 422, \
771, 43, 638, 801, 888, 85, 665, 618, 33, 72, 603, 226, 277, 56, 237, 986, \
605, 744, 79, 601, 122, 766, 910, 866, 651, 841, 486, 908, 692, 949, 98, \
384, 366, 84, 194, 264, 334, 368, 15, 80, 390, 433, 262, 865, 210, 899, \
994, 732, 408, 806, 702, 9, 913, 649, 972, 653, 21, 902, 903, 862, 385, \
686, 233, 816, 182, 471, 406, 784, 344, 435, 364, 50, 104, 858, 572, 31, \
457, 689, 547, 639, 376, 170, 377, 560, 459, 45, 810, 137, 103, 285, 464, \
324, 124, 818, 59, 677, 89, 234, 121, 802, 545, 900, 280, 579, 319, 132, \
327, 93, 877, 954, 466, 354, 779, 458, 916, 425, 863, 799, 167, 783, 856, \
855, 37, 361, 746, 484, 582, 758, 437, 317, 288, 32, 767, 447, 554, 837, \
701, 463, 706, 928, 672, 590, 918, 768, 725, 542, 169, 352, 753, 189, 593, \
245, 596, 254, 281, 567, 30, 562, 780, 157, 360, 936, 520, 184, 371, 668, \
859, 272, 883, 759, 193, 804, 396, 817, 983, 252, 73, 260, 893, 934, 754, \
533, 3, 426, 710, 365, 937, 693, 814, 315, 896, 28, 154, 333, 36, 970, \
997, 251, 57, 127, 944, 948, 13, 715, 271, 637, 961, 504, 643, 164, 472, \
996, 836, 847, 602, 306, 909, 770, 832, 681, 673, 326, 105, 18, 925, 51, \
106, 99, 148, 977, 755, 155, 907, 200, 485, 107, 999, 418, 844, 1, 316, 400, \
956, 528, 501, 77, 957, 583, 389, 309, 166, 798, 703, 81, 320, 993, 227, \
762, 705, 838, 220, 343, 395, 192, 323, 800, 679, 867, 708, 699, 743, 25, \
20, 946, 217, 26, 976, 654, 604, 881, 688, 687, 735, 530, 487, 482, 991, \
110, 839, 215, 391, 135, 175, 258, 278, 367, 387, 116, 963, 662, 118, 503, \
981, 444, 598, 438, 424, 505, 628, 912, 773, 661, 141, 1000, 328, 451, 265, \
63, 228, 500, 531, 629, 171, 581, 959, 731, 493, 794, 599, 206, 741, 180, \
667, 619, 808, 777, 966, 647, 44, 229, 405, 532, 24, 275, 421, 232, 273, \
920, 917, 353, 11, 606, 95, 990, 311, 249, 781, 636, 718, 551, 875, 402, \
592, 787, 230, 556, 985, 846, 145, 796, 971, 968, 483, 575, 987, 174, 75, \
239, 23, 250, 549, 372, 216, 563, 789, 313, 78, 322, 889, 290, 825, 22, 102, \
403, 611, 829, 729, 178, 499, 386, 716, 428, 454, 553, 657, 218, 979, 871, \
973, 988, 86, 587, 34, 635, 91, 757, 751, 674, 775, 880, 510, 61, 404, 821, \
873, 96, 65, 417, 66, 632, 238, 38, 882, 55, 644, 541, 739, 555, 879, 778, \
738, 823, 769, 524, 235, 452, 752, 717, 473, 730, 511, 978, 678, 10, 537, \
685, 188, 951, 621, 495, 212, 321, 359, 287, 350, 835, 713, 550, 370, 88, \
921, 82, 168, 415, 462, 443, 492, 573, 146, 763, 608, 407, 198, 236, 698, \
785, 809, 119, 196, 409, 469, 347, 756, 5, 358, 126, 381, 149, 943, 165, \
803, 90, 340, 887, 939, 927, 558, 848, 307, 337, 35, 645, 176, 489, 60, 812, \
995, 201, 114, 522, 312, 69, 952, 526, 820, 840, 411, 465, 17, 6, 648, 111, \
414, 901, 261, 815, 514, 764, 696, 41, 397, 655, 652, 728, 600, 747, 906, \
683, 719, 139, 383, 401, 765, 962, 259, 219, 617, 129, 734, 14, 612, 8, 214, \
958, 291, 518, 470, 47, 450, 782, 339, 656, 74, 670, 904, 431, 39, 399, 16, \
517, 224, 694, 857, 279, 507, 623, 338, 726, 434, 439, 630, 828, 737, 441, \
992, 589, 886, 248, 27, 853, 115, 591, 479, 923, 624, 267, 205, 289, 931, \
195, 733, 373, 453, 924, 420, 432, 282, 642, 329, 697, 805, 969, 52, 811, \
480, 922, 296, 615, 304, 627, 870, 597, 378, 666, 292, 663, 325, 242, 506, \
442, 515, 143, 101, 544, 849, 456, 791, 297, 965, 67, 270, 527, 574, 243, \
614, 253, 379, 356, 190, 594, 346, 742, 430, 87, 413, 427, 208, 4, 659, 607, \
850, 468, 502, 362, 231, 375, 774, 626, 793, 223, 125, 919, 308, 620, 255, \
748, 58, 557, 761, 942, 100, 186, 161, 71, 586, 727, 580, 892, 314, 878, \
509, 584, 914, 490, 177, 351, 891, 915, 845, 284, 750, 299, 830, 445, 221, \
336, 824, 588, 864, 536, 310, 736, 211, 876, 247, 525, 76, 548, 181, 974, \
980, 512, 476, 349, 565, 625, 709, 658, 263, 83, 191, 478, 535, 609, 508, \
244, 885, 257, 449, 534, 357, 660, 831, 144, 646, 935, 305, 776, 740, 671, \
631, 419, 302, 481, 410, 622, 286, 724, 113, 854, 108, 12, 932, 222, 675, \
874, 382, 578, 868, 332, 911, 561, 7, 207, 610, 138, 792, 213, 576, 152, \
938, 64, 131, 568, 348, 363, 745, 984, 187, 2, 564, 335, 834, 455, 42
It should be the correct answer, but checking each and every step with verbose mode is not viable.
We can have a look at it manually. That requires showing the number of taller persons in front. I have added a new command line option for this:
File: ordered-lineup-1000 (changes only)
unit sub MAIN (:$medium, :$large, :$verbose, :$verbose2, :$compare is copy);
say @result>>.height.join(", ");
say @result.map({ ": " ~ ++$ ~ ": " ~ $_.height ~ "[>" ~ $_.taller ~ "]"})
.join("\n") if $verbose2; # [1]
say @result>>.height eqv @A if $compare;
[1] Show the height followed by the number of taller
persons in front. Note the use of the anonymous state variable $
as a counter, without
an explicit declaration and initialisation.
See
docs.raku.org/syntax/$ for more
information about $
.
Checking all 1000 of them by hand isn't viable, so I'll do the first 10 only:
$ raku ordered-lineup-1000 --large --verbose2
...
: 1: 204[<0] # The tallest, so far.
: 2: 926[<0] # Ditto.
: 3: 704[<1] # #2 is taller, so ok.
: 4: 543[<2] # #2,3 are taller, so ok.
: 5: 760[<1] # #2 is taller, so ok.
: 6: 691[<3] # #2,3,5 are taller, so ok.
: 7: 795[<1] # #2 is taller, so ok.
: 8: 303[<6] # #2,3,4,5,6,7 are taller, so ok.
: 9: 136[<8] # #1-8 are taller, so ok.
: 10: 650[<5] # #2,3,5,6,7 are taller, so ok.
...
Only using the three sample sets given in the challenge isn't much fun. Here is a version where we specify the values on the command line:
File: ordered-lineup-dynamic
unit sub MAIN (:$H, :$T, :$A = "", :$verbose, :$verbose2); # [1]
my @H = $H.words>>.Int; # [2]
my @T = $T.words>>.Int; # [2]
my @A = $A.words>>.Int; # [2]
# die "Repeated heights" if @H.repeated; # [3]
die "H and T have different sizes" if @H.elems != @T.elems; # [4]
die "A have different size than H and T" if @A && @A.elems != @H.elems; # [5]
class Lineup
{
has Int $.height;
has Int $.taller;
}
my @list;
for ^@H -> $index
{
@list.push: Lineup.new(height => @H[$index], taller => @T[$index]);
}
my @result;
for @list.sort({ $^b.height <=> $^a.height }) -> $elem
{
my $taller = $elem.taller;
say ": H:{ $elem.height } -> T:{ $elem.taller }" if $verbose;
if (@result[$taller].defined)
{
@result.splice($taller, 1, $elem, @result[$taller]);
}
else
{
die "Not enough taller persons in front of { $elem.height }: \
{ @result.elems } (should have been $taller)"
if @result.elems != $taller;
@result.push: $elem;
}
}
say ": " ~ @result.raku if $verbose;
say @result>>.height.join(", ");
say @result.map({ ": " ~ ++$ ~ ": " ~ $_.height ~ "[<" ~ $_.taller
~ "]"}).join("\n") if $verbose2;
say @result>>.height eqv @A if @A; # [6]
[1] New command line options, replacing «:$medium», «:$large» and «:$compare».
[2] words
gives words, or strings. We must coerce
them to integers as the «Lineup» objects will choke if we create them with strings
values.
[3] This one is redundant. Duplicates can occur, even if they don&apso;t in the samples given with the challenge.
[4] Check the length of the height and taller lists.
[5] Check the length of the answer list, if given.
[6] Check that we have the correct answer, if we have an answer list.
See
docs.raku.org/routine/words
for more information about words
.
Running it, with the short sample set:
$ raku ordered-lineup-dynamic --H="2 6 4 5 1 3" --T="1 0 2 0 1 2"
5, 1, 2, 6, 3, 4
$ raku ordered-lineup-dynamic --H="2 6 4 5 1 3" --T="1 0 2 0 1 2"
--A="5 1 2 6 3 4"
5, 1, 2, 6, 3, 4
True
Note that it will compare the result with the given answer (in «@A»), if given.
Let us try tweaking the values:
$ raku ordered-lineup-dynamic --H="2 6 4 5 1 3" --T="1 0 2 0 5 2"
5, 2, 6, 3, 4, 1
$ raku ordered-lineup-dynamic --H="2 6 4 5 1 3" --T="1 0 2 0 6 2"
Not enough taller persons in front of 1: 5 (should have been 6)
$ raku ordered-lineup-dynamic --H="2 6 4 5 1 3" --T="1 1 2 0 1 2"
Not enough taller persons in front of 6: 0 (should have been 1)
$ raku ordered-lineup-dynamic --H="6 6 6 6 6 6" --T="0 0 0 0 0 0"
6, 6, 6, 6, 6, 6
The last one uses repetition ad absurdum, and works as we commented out the repetion check (in [3]).
And that's it.