• compare two sorted array, item by item, which one is bigger

    From hymie!@hymie@nasalinux.net to comp.lang.perl.misc on Sat Feb 24 19:14:49 2024
    From Newsgroup: comp.lang.perl.misc

    I'm sure this is an FAQ if I can just find the correct words to ask my question.

    I have two people, 0 and 1, which are denoted by the $player variable.

    I have a hash of sorted arrays

    @{$scores{$player}}
    104 92 92 90 87
    104 92 92 89 88

    And I have a %percent hash that holds the sum of those elements.
    In this case, $percent{$player} is 465 for both.

    (I can safely assume that all numbers are non-negative, so I'm fine with
    an empty value being treated as zero)

    (I also have a bunch of lousy code that I'm not proud of)

    So I have this construct

    foreach $player (sort {$percent{$b} <=> $percent{$a}} keys %percent)

    that will sort the %percent hash by value ... but since the two are
    equal, I think I'm getting a random choice.

    So then I wrote this construct

    foreach $player (sort
    {$percent{$b} <=> $percent{$a} || ${$scores{$b}}[0] <=> ${$scores{$a}}[0] } keys %percent)

    which will check the first element in each array from the %scores hash
    to see which value is larger.

    The question is -- how can I (or can I) programatically keep checking
    entries in the arrays of the %scores hash until I find a pair of
    entries that are not equal? I'd rather not have (in my case) 9
    individual tests of the items. Is there a simple subroutine I can
    use?

    My first thought is to compare $a[0] and $b[0] ... and then if they're
    the same, shift them both and try again. But I'm a little nervous that shifting the arrays will lose the values, and I don't want to do that.
    My second thought is use an array slice -- if $a[0] == $b[0] then
    recursively check $a[1-x] against $b[1-x] ...

    I'm hoping somebody knows something simpler.

    Thanks.

    --hymie! https://nasalinux.net/~hymie hymie@nasalinux.net --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rainer Weikusat@rweikusat@talktalk.net to comp.lang.perl.misc on Mon Feb 26 16:20:55 2024
    From Newsgroup: comp.lang.perl.misc

    hymie! <hymie@nasalinux.net> writes:
    I have two people, 0 and 1, which are denoted by the $player variable.

    I have a hash of sorted arrays

    @{$scores{$player}}
    104 92 92 90 87
    104 92 92 89 88

    And I have a %percent hash that holds the sum of those elements.
    In this case, $percent{$player} is 465 for both.

    [...]

    So I have this construct

    foreach $player (sort {$percent{$b} <=> $percent{$a}} keys %percent)

    that will sort the %percent hash by value ... but since the two are
    equal, I think I'm getting a random choice.

    So then I wrote this construct

    foreach $player (sort
    {$percent{$b} <=> $percent{$a} || ${$scores{$b}}[0] <=> ${$scores{$a}}[0] } keys %percent)

    which will check the first element in each array from the %scores hash
    to see which value is larger.

    The question is -- how can I (or can I) programatically keep checking
    entries in the arrays of the %scores hash until I find a pair of
    entries that are not equal?

    If you're arrays are always of equal length, you could use

    sub ary_cmp
    {
    my ($a0, $a1) = @_;
    my $rc;

    for (0 .. $#$a0) {
    $rc = $$a0[$_] - $$a1[$_];
    return $rc < 0 ? -1 : 1 if $rc;
    }

    return 0;
    }

    otherwise, it's a bit more difficult.

    sub ary_cmp
    {
    my ($a0, $a1) = @_;
    my ($last, $rc);

    $last = $#$a0;
    $_ < $last and $last = $_ for $#$a1;

    for (0 .. $last) {
    $rc = $$a0[$_] - $$a1[$_];
    return $rc < 0 ? -1 : 1 if $rc;
    }

    return @$a0 <=> @$a1;
    }

    could do.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rainer Weikusat@rweikusat@talktalk.net to comp.lang.perl.misc on Tue Feb 27 15:37:33 2024
    From Newsgroup: comp.lang.perl.misc

    Rainer Weikusat <rweikusat@talktalk.net> writes:
    hymie! <hymie@nasalinux.net> writes:

    [...]

    foreach $player (sort
    {$percent{$b} <=> $percent{$a} || ${$scores{$b}}[0] <=> ${$scores{$a}}[0] } >> keys %percent)

    which will check the first element in each array from the %scores hash
    to see which value is larger.

    The question is -- how can I (or can I) programatically keep checking
    entries in the arrays of the %scores hash until I find a pair of
    entries that are not equal?

    If you're arrays are always of equal length, you could use

    sub ary_cmp
    {
    my ($a0, $a1) = @_;
    my $rc;

    for (0 .. $#$a0) {
    $rc = $$a0[$_] - $$a1[$_];
    return $rc < 0 ? -1 : 1 if $rc;
    }

    return 0;
    }

    This can be simplified somewhat by using the <=> operator for the check
    inside the loop as that already produces the desired result of either
    -1, 0 or 1.

    sub ary_cmp
    {
    my ($a0, $a1) = @_;

    for (0 .. $#$a0) {
    $_ and return $_ for $$a0[$_] <=> $$a1[$_];
    }

    return 0;
    }
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From hymie!@hymie@nasalinux.net to comp.lang.perl.misc on Wed Feb 28 15:35:22 2024
    From Newsgroup: comp.lang.perl.misc

    In our last episode, the evil Dr. Lacto had captured our hero,
    Rainer Weikusat <rweikusat@talktalk.net>, who said:
    hymie! <hymie@nasalinux.net> writes:
    The question is -- how can I (or can I) programatically keep checking
    entries in the arrays of the %scores hash until I find a pair of
    entries that are not equal?

    If your arrays are always of equal length, you could use

    I don't think I can depend on that :(

    otherwise, it's a bit more difficult.

    sub ary_cmp
    {
    my ($a0, $a1) = @_;
    my ($last, $rc);

    # we need the length of the shorter array
    # start with the length of array a0
    # and see if the length of array a1 is less
    $last = $#$a0;
    $_ < $last and $last = $_ for $#$a1;

    # for each entry in the shorter array
    # compare that numbered entry in the two arrays
    # return <=> if the result is not 0
    for (0 .. $last) {
    $_ and return $_ for $$a0[$_] <=> $$a1[$_];
    }

    # all of the elements are equal, so return the longer array
    return @$a0 <=> @$a1;
    }

    I took the liberty of adding your improvement to this function.

    I'll definitely try this out and see how well it work.

    I have a few followup questions...

    (*) I added some comments. Can you tell me if I'm correct?

    (*) Could I have set $last this way?

    $last = $#$a0 < $#$a1 ? $#$a0 : $#$a1 ;

    or

    $last = @$a0 < @$a1 ? @$a0 : @$a1 ;

    ?

    (*) In this construct

    for (0 .. $last) {
    $_ and return $_ for $$a0[$_] <=> $$a1[$_];

    is it safe to reuse $_ like that? The scoping will work itself out,
    even without a bracket set?

    Thank you very much.

    --hymie!
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rainer Weikusat@rweikusat@talktalk.net to comp.lang.perl.misc on Wed Feb 28 16:34:35 2024
    From Newsgroup: comp.lang.perl.misc

    hymie! <hymie@nasalinux.net> writes:
    In our last episode, the evil Dr. Lacto had captured our hero,
    Rainer Weikusat <rweikusat@talktalk.net>, who said:
    hymie! <hymie@nasalinux.net> writes:
    The question is -- how can I (or can I) programatically keep checking
    entries in the arrays of the %scores hash until I find a pair of
    entries that are not equal?

    If your arrays are always of equal length, you could use

    I don't think I can depend on that :(

    otherwise, it's a bit more difficult.

    sub ary_cmp
    {
    my ($a0, $a1) = @_;
    my ($last, $rc);

    # we need the length of the shorter array
    # start with the length of array a0
    # and see if the length of array a1 is less
    $last = $#$a0;
    $_ < $last and $last = $_ for $#$a1;

    # for each entry in the shorter array
    # compare that numbered entry in the two arrays
    # return <=> if the result is not 0
    for (0 .. $last) {
    $_ and return $_ for $$a0[$_] <=> $$a1[$_];
    }

    # all of the elements are equal, so return the longer array
    return @$a0 <=> @$a1;
    }

    I took the liberty of adding your improvement to this function.

    I'll definitely try this out and see how well it work.

    I have a few followup questions...

    (*) I added some comments. Can you tell me if I'm correct?

    Yes.


    (*) Could I have set $last this way?

    $last = $#$a0 < $#$a1 ? $#$a0 : $#$a1 ;

    or

    $last = @$a0 < @$a1 ? @$a0 : @$a1 ;

    The first yes, second no as $#$a0 == @$a0 - 1. It's just syntactically a
    bit more repetitive.


    ?

    (*) In this construct

    for (0 .. $last) {
    $_ and return $_ for $$a0[$_] <=> $$a1[$_];

    is it safe to reuse $_ like that?

    Yes. The foreach-for aliases a localized $_ to the first element and
    then executes the loop body, ie, either the block in case of for (...) {
    } or the statement for the statement modifier. Then, it does the same
    with the second element and so forth, until the body has been run for
    all list elements. This means $_ reverts back to the current index after
    the

    $_ and return $_

    has been executed.
    --- Synchronet 3.20a-Linux NewsLink 1.114