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

I have a string that contains 60 characters that I would like to 1) extract one at a time, 2) perform a function on (subtract 64 from the characters' ord) the extracted character and 3) place the result in a list of lists, where each of 20 elements would have 3 elements. The characters in the string are in the order they need to be in the list of lists, thusly (without the function performed on them):

@result = ( ['B', 'E', 'H'], ['A', 'C', 'J'],...); etc. etc.

I have poured through my Perl books (Advanced Perl Programming, Intermediate Perl Programming, The Perl Cookbook, etc.) and the solution hasn't presented itself to me. It's probably there, but I am not seeing it...

I do have my own solution, but in my quest for learning, I would like a more Perlish way of performing the task. Here is what I have :
#! /usr/local/bin/perl use strict; use warnings; # Setup the situation my $string = "BEHACJBDLCENADFEGOFHQAGIHJRBIKJLSCKMLNTDMOFNPOQTGPRIQSKR +TMPS"; my ($i, $j, @array, @result); # Perform the task @array = split //, $string; for $i (1..20) { for $j (1..3) { $result[$i][$j] = ord(shift(@array)) - 64; } } # Print the results for $i (1..20) { for $j (1..3) { print sprintf("%02d", $result[$i][$j]) . " "; } print "\n"; }
Because of legacy issues, I have loaded the @result array at the first element rather than the zeroth. This isn't a huge deal, but it does eliminate the need to adjust the other parts of the program.

I have attempted all manner of split, map, grep, substituions, etc. The substitution worked fine for me, but getting the results into the list of lists proved to be too much for me.

Since I actually have it doing what I want, and performance isn't really an issue, should I even be worried about devising a "one-liner" that would perform the task in one swell foop? I guess I think I should worry about...

Thanks! And another tip o' the hat to anyone who recognizes the format and the numbers in @result...

tjg...whiskey one sierra delta mike

Replies are listed 'Best First'.
Re: Split a string into a list of lists
by GrandFather (Saint) on Feb 06, 2009 at 06:45 UTC
    my $string = "BEHACJBDLCENADFEGOFHQAGIHJRBIKJLSCKMLNTDMOFNPOQTGPRIQSKR +TMPS"; my @array = (undef); push @array, [0, map {ord ($_) - 64} split '', substr $string, 0, 3, ' +' ] while length $string; printf "%02d %02d %02d\n", @{$_}[1..3] for @array[1..$#array];

    gives identical results when compared with your sample and may give you a little something to think about.


    Perl's payment curve coincides with its learning curve.
Re: Split a string into a list of lists
by fullermd (Vicar) on Feb 06, 2009 at 06:10 UTC
Re: Split a string into a list of lists
by BrowserUk (Patriarch) on Feb 06, 2009 at 10:20 UTC
    #! perl -slw use strict; use Data::Dump qw[ pp ]; my $string = "BEHACJBDLCENADFEGOFHQAGIHJRBIKJLSCKMLNTDMOFNPOQTGPRIQSKR +TMPS"; my @results = map [ split''], unpack '(a3)*', $string; unshift @results, undef; ## Make the data start at index 1 pp \@results; __END__ c:\test>junk4 [ undef, ["B", "E", "H"], ["A", "C", "J"], ["B", "D", "L"], ["C", "E", "N"], ["A", "D", "F"], ["E", "G", "O"], ["F", "H", "Q"], ["A", "G", "I"], ["H", "J", "R"], ["B", "I", "K"], ["J", "L", "S"], ["C", "K", "M"], ["L", "N", "T"], ["D", "M", "O"], ["F", "N", "P"], ["O", "Q", "T"], ["G", "P", "R"], ["I", "Q", "S"], ["K", "R", "T"], ["M", "P", "S"], ]

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Split a string into a list of lists
by johngg (Canon) on Feb 06, 2009 at 10:20 UTC

    This method invokes an on-the-fly subroutine with the string split into characters as the arguments. This allows you to shift groups of three letters at a time using a map, with a second map to do the subtraction, all inside an array ref. constructor which is pushed onto the return array.

    use strict; use warnings; my $string = q{BEHACJBDLCENADFEGOFHQAGIHJRBIKJLSCKMLNTDMOFNPOQTGPRIQSKRTMPS}; my @array = sub { my @arr = ( [] ); push @arr, [ map ord( $_ ) - 64, map shift, 1 .. 3 ] while @_; return @arr; }->( split m{}, $string ); printf qq{%02d %02d %02d\n}, @$_ for @array[ 1 .. $#array ];

    The results match yours and I think the numbers may very well have something to do with the England cricketer's recent batting form :-(

    Cheers,

    JohnGG

Re: Split a string into a list of lists
by AnomalousMonk (Archbishop) on Feb 06, 2009 at 09:49 UTC
    Don't know if this is more Perlish, but this also seems to give the same results, also produces 'null' zeroth elements:
    use strict; use warnings; my $string = "BEHACJBDLCENADFEGOFHQAGIHJRBIKJLSCKMLNTDMOFNPOQTGPRIQSKRTMPS"; my @result = ( [ 0, 0, 0, 0, ], # map [ 0, map $_ -= 64, unpack 'C*', $_ ], # -= is pointless map [ 0, map $_ - 64, unpack 'C*', $_ ], unpack '(a3)*', $string ); # Print the results for my $i (1..20) { for my $j (1..3) { print sprintf("%02d", $result[$i][$j]) . " "; } print "\n"; }
    Output:
    02 05 08 01 03 10 02 04 12 03 05 14 01 04 06 05 07 15 06 08 17 01 07 09 08 10 18 02 09 11 10 12 19 03 11 13 12 14 20 04 13 15 06 14 16 15 17 20 07 16 18 09 17 19 11 18 20 13 16 19
    Update: $_ -= 64 in map statement is pointless waste of computrons. Changed to $_ - 64 per oshalla's reply below.
Re: Split a string into a list of lists
by gone2015 (Deacon) on Feb 06, 2009 at 10:32 UTC

    If you really want to do it in one, then the following is a way:

    my @array = (undef, map([undef, map($_-64, unpack('C3', $_))], unpack('(A3)*', $string)) +) ;
    The thing to watch out for with nested maps is the reuse of $_, but in this case it's fine.

    Just for the hell of it, I threw a little benchmarking at it:

                    Rate       W1SDM GrandFather        GMCH
      W1SDM       7.59/s          --        -30%        -48%
      GrandFather 10.9/s         43%          --        -25%
      GMCH        14.6/s         93%         34%          --
    
    which tells you that this can go almost twice as fast if you throw certain amount of magic at it. The question is: does this affect the price of cocoa ? And the other question is: if so, was the effect worth the effort ?

    Update: ah... same trick as AnomalousMonk above.