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

Dear Masters,

My code below decompose a string into tuples (pair).
It already does the job, but to me it seems very slow and clumsy.

Is there a faster and more compact way to do it?
See the result below for answers:
use Data::Dumper; my $s1 = 'X -4 Y 3 Z'; my $s2 = 'W 1 X -4 Y 3 Z'; my $s3 = 'X 2 Y'; my $s4 = 'A -4 B -4 C -4 A -4 B'; decomp_str($s1); decomp_str($s2); decomp_str($s3); decomp_str($s4); sub decomp_str { my $s1 = shift; my @ar = split (" ",$s1); my @dcomp; push @dcomp, "$ar[0] $ar[1] $ar[2]"; foreach my $i ( 2 .. $#ar-1 ) { if ($ar[$i] =~ /[A-Z]+/) { push @dcomp, "$ar[$i] $ar[$i+1] $ar[$i+2]"; } } print Dumper \@dcomp; return \@dcomp; }
The answers are:
$VAR1 = ['X -4 Y','Y 3 Z']; # For $s1 $VAR1 = ['W 1 X','X -4 Y','Y 3 Z']; # For $s2 $VAR1 = ['X 2 Y']; # For $s3 $VAR1 = ['A -4 B','B -4 C','C -4 A','A -4 B']; # For $s4


---
neversaint and everlastingly indebted.......

Replies are listed 'Best First'.
Re: Decompose a String into Tuples (Faster and Compact Way)
by hv (Prior) on Oct 12, 2005 at 16:48 UTC

    Guessing at the definition of the problem based on your examples, here's one simple way:

    sub decomp_str { my $str = shift; $str =~ s/\s([a-z])\s/$1;$1/gi; [ split /;/, $str ]; }

    Hugo

Re: Decompose a String into Tuples (Faster and Compact Way)
by Roy Johnson (Monsignor) on Oct 12, 2005 at 16:50 UTC
    This is nearly the same as what you're doing. The difference is that you will not start a group with the 2nd item.
    sub decomp_str { # Capture three starting with alpha, but advance only one [$_[0] =~ /(?=([a-z]\s*(?:\S+\s*){2}))\S+\s*/gi ] }
    Updated, but still untested.

    Caution: Contents may have been coded under pressure.
      Dear Roy Johnson,

      Taking the benefit of incredible speed that your code provide. How can I modify your code above such that given this string:
      my $s1 = 'X -4 Y 3 Z';
      It will decompose into a such AoA:
      $VAR = [["X", -4, "Y"], ["Y",3,"Z"]];
      Thanks so much beforehand. Really hope to hear from you again.

      ---
      neversaint and everlastingly indebted.......
        You just want to split each of the extracted tuple-strings:
        my $aref = [ map [split], $s1 =~ /(?=([a-z]\s*(?:\S+\s*){2}))\S+\s*/gi + ];

        Caution: Contents may have been coded under pressure.
Re: Decompose a String into Tuples (Faster and Compact Way)
by TedPride (Priest) on Oct 12, 2005 at 16:57 UTC
    use strict; use warnings; use Data::Dumper; while (<DATA>) { chomp; print Dumper decomp($_); } sub decomp { my @ret; @_ = split / /, $_[0]; for (my $i = 0; $i < $#_; $i += 2) { push @ret, [$_[$i], $_[$i+1], $_[$i+2]]; } return \@ret; } __DATA__ X -4 Y 3 Z W 1 X -4 Y 3 Z X 2 Y A -4 B -4 C -4 A -4 B
      (Um, for the above to do what the op wants, I think you need something like  push @ret, "@_[$i..$i+2]"; instead of  push @ret, [$_[$i], $_[$i+1], $_[$i+2]];)

      Here's my shifty suggestion:

      sub decomp { @_ = split / /, shift; return [ map { join " ", (shift, shift, $_[0]) } (1..@_/2) ]; }
      update: shorter and generalizable to producing any size tuple:
      sub decomp { my $n = 2; # for n+1-uples @_ = split / /, shift; return [ map { $_*=$n; "@_[$_..$_+$n]" } 0..@_/$n-1 ]; }
Re: Decompose a String into Tuples (Faster and Compact Way)
by Anonymous Monk on Oct 12, 2005 at 19:36 UTC
    $ perl -le' my $s1 = q/X -4 Y 3 Z/; my $s2 = q/W 1 X -4 Y 3 Z/; my $s3 = q/X 2 Y/; my $s4 = q/A -4 B -4 C -4 A -4 B/; for my $s ( \( $s1, $s2, $s3, $s4 ) ) { print for $$s =~ /(?=(\S+\s+-?\d+\s+\S+))/g; } ' X -4 Y Y 3 Z W 1 X X -4 Y Y 3 Z X 2 Y A -4 B B -4 C C -4 A A -4 B

      Is there a reason for the \$s1 stuff? It seems to work just fine without....

      #!perl -l my $s1 = q/X -4 Y 3 Z/; my $s2 = q/W 1 X -4 Y 3 Z/; my $s3 = q/X 2 Y/; my $s4 = q/A -4 B -4 C -4 A -4 B/; for ( $s1, $s2, $s3, $s4 ) { print for /(?=(\S+\s+-?\d+\s+\S+))/g; }

      Don't forget that for aliases its iterator, which is basically a funny kind of reference that doesnt need to be dereferenced.

      ---
      $world=~s/war/peace/g

Re: Decompose a String into Tuples (Faster and Compact Way)
by Moron (Curate) on Oct 13, 2005 at 13:06 UTC
    A lexer-ish approach:
    use Data::Dumper; my $s1 = 'X -4 Y 3 Z'; my $s2 = 'W 1 X -4 Y 3 Z'; my $s3 = 'X 2 Y'; my $s4 = 'A -4 B -4 C -4 A -4 B'; decomp_str($s1); decomp_str($s2); decomp_str($s3); decomp_str($s4); sub decomp_str { my @dcomp; $_ = shift; while( /^(\S\s+\S+\s+)(\S)(.*)$/ ) { push @dcomp, $1 . $2; $_ = $2 . $3; } ( length() == 1 ) or die( "Syntax error: $_\n" ); print Dumper \@dcomp; return \@dcomp; }

    -M

    Free your mind