Compared Lineup with Raku

by Arne Sommer

Compared Lineup with Raku

[70] Published 1. May 2020.

This is my response to the Perl Weekly Challenge #58.

Challenge #58.1: Compare Version

Compare two given version number strings v1 and v2 such that:

  • If v1 > v2 return 1
  • If v1 < v2 return -1
  • Otherwise, return 0

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:

   v1   v2    Result
------ ------ ------
  0.1 < 1.1     -1
  2.0 > 1.2      1
  1.2 < 1.2_5   -1
1.2.1 > 1.2_1    1
1.2.1 = 1.2.1    0

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:

File: comp-ver
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».

File comp-ver-int
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.

Less is More

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

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] );

PHP

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

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

Python doesn't have a three way comparison operator, so we have to write some more code:

File: comp-ver.py
from 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"

Wrapping it all up

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.

Challenge #58.2: Ordered Lineup

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:

  • @H = (2, 6, 4, 5, 1, 3) # Heights
  • @T = (1, 0, 2, 0, 1, 2) # Number of taller people in front

The ordering of both arrays lines up, so H[i] and T[i] refer to the same person. For example, there are 2 taller people in front of the person with height 4, and there is 1 person in front of the person with height 1.

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 (5, 1, 2, 6, 3, 4) in this case. (The leftmost element is the “front” of the array.)

Here’s a 64-person example, with answer provided:

# Heights
@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);

# Number (of) taller people in front
@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);

# Expected answer
@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);

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.