Category: Utility Scripts
Author/Contact Info Adrien 'Axioplase::Pied' /msg me
Description: I sometimes miss Ocaml while parsing lists... Yesterday, I needed something that would take a list of tuples and give me back the list of all the nth elements of each tuple. Here we go! Update: changed the name, as graff showed out it was just a kind of matrix transposer in fact :)

use strict;
#The Function
sub demux {
  my ($rsrc,$separator)=@_; #a reference to the list of tuples, and th
+e separator
  my @res; # result
  my $nbelem=@{[split /$separator/, $$rsrc[0]]}; #this may be a little
+ bit *gruik* to get the number of elements in each tuple...
  foreach my $n (0.. $nbelem-1){
    push @res , [(map { (split /$separator/ )[$n] } @{$rsrc} )]; #that
+ took me ages to figure out :D
  return @res; #guess what!

#The Test

sub even { #self explanatory
  return $_ if ($_=int(rand(100)),$_%2==0);
  return $_+1;
sub odd { #worse :D
  return &even+1;

my @liste;
push @liste, (&odd.",".&even.",".&odd.",".&even.",".&odd.",".&even) fo
+reach(1..10); #create a list of even and odd numbers alternating (so 
+that we understand easily)
print "$_\n" foreach (@liste); #print it out
print "\n";

my @re= demux(\@liste,","); # it's a kind of magic

print "@{$_}\n" foreach(@re); # Yehaaaaww!!
Let's run it:
C:\Documents and Settings\xxxx\My Documents\perl>""

85 73 89 45 99 85 45 57 61 37
34 38 10 68 100 50 8 10 36 16
15 31 31 23 27 75 45 5 1 71
38 92 96 90 52 28 48 4 22 32
53 17 35 33 25 73 55 43 7 81
80 96 60 12 28 4 66 20 74 42

looks like it worked.

Now, the explanation, for those who are as good as I am in Perl, which means that they *may* need to read this or use perldoc for a few hours before understanding the code ^^
(yes, I'm an eternal newbie)

my ($rsrc,$separator)=@_;
first argument is a reference to the tuples list second is the separator of each element of the tuple.

my @res;
will be a list of list references.

my $nbelem=@{[split /$separator/, $$rsrc[0]]};
I want to know how many elements are in each tuple. I had to use an anonymous array, because perl complained when I had
that using "@_" implicitely was deprecated.
Since $rsrc is a ref to an array, $$rsrc[0] is that array's first element (here, a tuple)

push @res , [(map { (split /$separator/ )[$n] } @{$rsrc} )];
split /$separator/
Split the tuple
get the n+1th element of the tuple (remember arrays start at index zero)
(map{(split /$separator/ )[$n] }@{$rsrc})
apply this to every element of the source list.
Since we use a reference, I tell Perl this is an array ref with the syntax @{$arrayref}
[(map { (split /$separator/ )[$n] } @{$rsrc} )];
Make this brand new shiny list a reference
push @res , [(map { (split /$separator/ )[$n] } @{$rsrc} )];
so that we can add it to our result list (otherwise, we'd get a flattened list, instead of a list of lists)

As you see with the output, we got what we wanted.
I hope this could will be useful (and added to List::Utils or Perl6 after cleaning/rewriting? I didn't search a lot, but there didn't seem to be such a module on CPAN) and that my explainations will help those who, like me, are still a bit afraid of lists of lists and references...


Replies are listed 'Best First'.
Re: Demuxer
by graff (Chancellor) on Jul 13, 2005 at 03:32 UTC
    I'm tempted to suggest that "transposer" would be a better name than "demuxer", because it seems like the basic result is to transpose a matrix of values. But I realize you're also converting a list of "xSV" text-string rows into a transposed matrix -- two operations in one function.

    Some purists would point out that it's better to keep the two operations distinct. (Converting a list of xSV strings into a matrix is pretty simple and applies to lots of situations, and the same is true for transposing a matrix, so why not just use the two simple functions one after the other, rather than create a relatively complex function to do both things at once?) Their argument would be that the single-job functions are easier to create, maintain and use because they are simpler in all respects -- apart from the fact that these particular two functions have already been solved, probably numerous times in different ways, in existing modules and common programming idioms.

    Still, that is certainly an educational use of "map". It may not be optimal in terms of run-time or memory usage (I don't know, I haven't tried to benchmark it against any alternative, but I'll bet a faster technique can be found), but it is worth studying.

Re: Transposer
by jdporter (Chancellor) on Jul 13, 2005 at 15:30 UTC
    You said your motivation was a need to
    take a list of tuples and give back the list of all the nth elements of each tuple
    but the code you posted doesn't do exactly that. (It transposes a matrix, as already discussed.) So here's code that does exactly that.
    # first arg: n # second arg: array(ref) of arrays. # return: list of nth elements. sub nth_elem { map $_->[$_[0]], @{$_[1]} }
    Trivial, as you can see.
      Well, it's not a matrix which is input, but it is a matrix that is output. input is a list of strings, with each string defining a tuple, whereas output is a LoL. Anyway, thanks for the code.
        Yeah... but as already discussed, the part that converts those strings into actual arrays should probably be separated from the other part. But hey - if they must be integral, then here:
        # first arg: n # second arg: array(ref) of strings which are comma-separated lists. # return: list of nth elements. sub nth_elemS { map { (split ',')[$_[0]] } @{$_[1]} }
Re: Transposer
by eric256 (Parson) on Jul 14, 2005 at 15:12 UTC

    A pugs version of transpose..

    use v6; my $matrix = [ [85,34,15,38,53,80], [73,38,31,92,17,96], [89,10,31,96,35,60], [45,68,23,90,33,12], [99,100,27,52,25,28], [85,50,75,28,73,4], [45,8,45,48,55,66], [57,10,5,4,43,20], [61,36,1,22,7,74], [37,16,71,32,81,42], ]; sub print_matrix ($matrix) { for ($matrix) -> $row { say $row.join(" "); } } sub transpose ($matrix) { my $new_matrix; my $width = $matrix[0].elems - 1; my $height = $matrix.elems - 1; for (0 .. $width) -> $y { for (0 .. $height) -> $x { $new_matrix[$y][$x] = $matrix[$x][$y]; } } return $new_matrix; } say "before"; print_matrix($matrix); say "after"; print_matrix( transpose $matrix );

    Eric Hodges