Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello

I want to iterate through a list of inputs in a file and create a list of tuples e.g. <1,5>, <7,10>, <12,20>. There will be one tuple per like. I would then like to order the tuples and then iterate through these once sorted. The second item in a tuple will always be higher than the first, and i want them ordered by the first item in a tuple. I didn't know what data structure I could use to capture pairs of data points that I could then order. I'm a beginner at perl so I don't know how to create a custom data structure.

thanks for your help

Replies are listed 'Best First'.
Re: beginner - ordering tuples
by LanX (Saint) on Nov 13, 2010 at 01:08 UTC
    This example simplifies your external file to a DATA-section.

    use strict; use warnings; my @datas; #- parse input for (<DATA>){ my @entries = m/<(\d+),(\d+)>/; push @datas, [@entries]; } #- sort @datas = sort { $a->[0] <=> $b->[0] } @datas; #- output use Data::Dumper; print Dumper \@datas; __DATA__ <12,20> <1,5> <7,10>

    see perllol explanation of the datastructure (Array Of Arrays) and check how to apply the sort command.

    Cheers Rolf

      thanks to you both. I can follow the second answer but would never have thought do it like that.
Re: beginner - ordering tuples
by Khen1950fx (Canon) on Nov 13, 2010 at 08:43 UTC
    Trying to make it as simple as possible, I used List::Tuples and the builtin sort function.

    First, we'll assume that the inputs are unordered, so we'll use a numerically-ascending sort:

    my @sorted_numbers = sort {$a <=> $b} @numbers
    Then diving head-first into List::Tuples:
    #!/usr/bin/perl use strict; use warnings; use List::Tuples qw(:all); use Data::Dumper::Concise; my @numbers = qw(10 1 12 5 20 7); my @sorted_numbers = sort { $a <=> $b } @numbers; my @tuples = tuples[2] => (@sorted_numbers); print Dumper(@tuples);
      Thanks for your reply. I've actually used the solution given above my rolf and it works great. However I have now found an additional need for the ordering: If some tuples have the same number on the first position I need them to be ordered so that the HIGHEST number in the second position comes first
      e.g. <12, 34> <12, 43> <12,10> becomes <12,43> <12,34> <12,10>
      I couldn't modify the code to get it to do what I wanted
        do you mind if I simplify it to a one liner?

        use strict; use warnings; my @datas; # for (<DATA>){ # push @datas, [ m/<(\d+),(\d+)>/ ]; # } @datas = sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] } map { [ m/<(\d+),(\d+)>/ ] } <DATA>; use Data::Dumper; print Dumper \@datas; __DATA__ <7,22> <12,20> <7,15> <1,5> <7,10>

        please see sort about how the boolean three states condition decides about the ordering.

        the perlop || is - like in C - short circuiting, that is the RHS is only evaluated (and returned) when the LHS is false (i.e 0 if <=> compares equal values).

        If your tuples have more than 2 elements you should ask for a generic solution.

        Cheers Rolf

        I modified the above as follows but it will probably make you perl programmers cringe with despair. Sorry rolf for desecrating your original answer :)
        use strict; use warnings; $" = ', '; my @datas; #- parse input for (<DATA>){ my @entries = m/<(\d+),(\d+)>/; push @datas, [@entries]; } #- sort @datas=sort sort_test @datas; #- output use Data::Dumper; print Dumper \@datas; sub sort_test() { if ($a->[0] < $b->[0]) { return -1; }elsif ($a->[0] > $b->[0]){ return 1 }else { if ($a->[1] > $b->[1]) { return -1; } elsif ($a->[1] < $b->[1]) { return 1; } else { return 0; } } } __DATA__ <12,20> <1,5> <7,10> <7,12> <7,15>
Re: beginner - ordering tuples
by Anonymous Monk on Nov 13, 2010 at 00:58 UTC
    If the file is like 7, 10\n, maybe
    open(my $filehandle, '<', $filename) or die "error opening $filename: $!"; my @pairs = sort { $$a[0] <=> $$b[1] } map { chomp; [ split /\s*,\s*/ ] } grep { /\S/ } # skip empty lines <$filehandle>;
      I'm so much of a beginner I can't follow that. I will try and work it out but any help is appreciated.
        the input file isn't in that format. i will have the values in 2 variables called $a and $b after i have parsed the line and i know $b will be higher than a for every row.
Re: beginner - ordering tuples
by 7stud (Deacon) on Nov 13, 2010 at 21:58 UTC

    Here's another version that might be easier for you to understand:

    my @array_of_references; for my $line (<DATA>) { my @values = split ' ', $line; push @array_of_references, \@values; #The \ creates a reference. } my @sorted_refs = sort by_my_custom_func @array_of_references; sub by_my_custom_func { if ($a->[0] < $b->[0]) {return -1} # $a should come before $b elsif ($a->[0] > $b->[0]) {return +1} # $a should come after $b #Execution arrives here only if $a->[0] equals #$b->[0]: if ($a->[1] < $b->[1]) {return +1} # $a should come after $b elsif ($a->[1] > $b->[1]) {return -1} # $a should come before $b else {return 0} } for my $arr_ref (@sorted_refs){ print "@$arr_ref" . "\n";} __DATA__ 7 22 12 20 7 15 1 5 7 10 --output:-- 1 5 7 22 7 15 7 10 12 20

    perl's sort function can be a little hard to understand. As far as I can tell, the value you return from a custom sort function always determines what happens to $a. If you return a negative number, then $a will come before $b in the sorted results; and if you return a positive number then $a will come after $b in the sorted results.

    Note that the <=> operator is just a shortcut for:

    if ($a < $b) {return -1} elseif ($a > $b) {return +1} else {return 0}

    However if you reverse the order of $a and $b, e.g. $b <=> $a, you get a reverse sort. That's because when sort calls the <=> operator, otherwise known as the "spaceship operator", sort calls it like this:

    $a = 1; $b = 2; my $result1 = spaceship($a, $b); print $result1 . "\n"; my $result2 = spaceship($b, $a); print $result2 . "\n"; sub spaceship { my $x = shift; my $y = shift; if ($x < $y) {return -1} elsif ($x > $y) {return +1} else {return 0}; } --output:-- -1 1

    Remember, sort() uses the return value from the sort function, in this case <=>, to determine what to do with $a. In the first case, $a <=> $b, the -1 tells sort() that $a should go before $b, and in the second case, $b <=> $a, the 1 tells sort() that $a goes after $b. In short, the <=> operator always thinks what is on its right is $a and what's on its left is $b.