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

Hello Monks, I have a working script to find the difference between two sets of strings. I found some good documentation and examples of traverse sequences but it has some code in it that I don't understand. It works, but I would like to know what is going on. Here is the code snippet:
my @old = split(/\s+/, $old); my @new = split(/\s+/, $new); traverse_sequences(\@old, \@new, { MATCH => sub { print OUTPUT shift(@old)."\n"}, DISCARD_A => sub { print OUTPUT "<strong><font color=red>".@old +->[shift]."</font></strong>\n"}, DISCARD_B => sub { print "<strong><font color=blue>".@new->[sh +ift, shift]."</font></strong>\n"}, } );
Basically just feeding in two string array references, but the @new->shift, shift is what I do not understand. At first I thought it is equivalent to shift(@new).shift(@new) but it is not. @old->shift seems similar to shift(@old) but looking at it closer, it yields different results as well... Thank you for any wisdom you can share!

Replies are listed 'Best First'.
Re: Traverse Sequences in Algorithm::Diff module
by believer (Sexton) on Apr 21, 2011 at 14:43 UTC
    On shift:
    If ARRAY is omitted, shifts the @_ array within the lexical scope of subroutines and formats...

    So shift works on @_, that means shift, shift is a list containing the first two arguments that traverse_sequences hands to the callback function. docs on traverse_sequences was too long a read for me at the moment. You should be able to find the answer in there.
Re: Traverse Sequences in Algorithm::Diff module
by InfiniteSilence (Curate) on Apr 21, 2011 at 14:38 UTC

    I get a warning that the specfied usage is deprecated:

    perl -we "@alpha = qw|d e f g h|; print @alpha->[shift,shift];" Using an array as a reference is deprecated at -e line 1. Use of uninitialized value in array element at -e line 1.

    When I ignore the warnings I get 'd' with the above, so 'works' means it is not doing what I would think it should do and returns the first and second elements:

    perl -e "@alpha = qw|d e f g h|; print (shift @alpha, shift @alpha);"

    Perhaps it will work in different environments though. I'm running This is perl, v5.8.0 built for MSWin32-x86-multi-thread

    Also, the pod doesn't have that as an example...where did you get that example from?

    Celebrate Intellectual Diversity

Re: Traverse Sequences in Algorithm::Diff module
by Khen1950fx (Canon) on Apr 21, 2011 at 16:18 UTC
    I couldn't find any examples of "shift, shift", so I'll assume .shift(@old) and .shift(@new). For MATCH =>, etc., I used callbacks instead of subs. I tried to use as much of the original code as I could and added a few other things. I think that you wanted something like this:
    #!/usr/bin/perl use strict; use warnings; use CGI qw(:standard :html3); use Algorithm::Diff qw(traverse_sequences); use Text::Tabs; my $old = '1,2,3,4'; my $new = '5,6,7,8'; my @old = split( /\s+/, $old ); my @new = split( /\s+/, $new ); my $style = <<EOS; PRE { margin-left: 24pt; font-size: 12pt; font-family: Courier, monospaced; white-space:pre } PRE.onlynew { color: red } PRE.onlyold { color: blue } EOS print start_html( { -title => "$old vs $new", -style => { -code => $style } } ), h1( { -style => 'margin-left: 24pt' }, span( { -style => 'color: red' }, $old ), span(" <i>vs.<i> "), span( { -style => 'color: blue' }, $new ) ), "\n"; traverse_sequences( \@old, \@new, { MATCH => \&match, DISCARD_A => \&only_old, DISCARD_B => \&only_new, } ); print end_html; sub match { print @old, "\n"; } sub only_old { print "<strong><font color=red>" . shift(@old) . "</font></strong> +\n"; } sub only_new { print "<strong><font color=blue>" . shift(@new) . "(</font></stron +g>\n"; }
      Well thank you everyone for your wisdom! Khen1950fx, I used your code to find a shift(array) method that works. By adding a shift() to each traverse line one by one and checking the results, I found out what I think the issue was. When a match is found, not only do we want to print the match by shifting the new array but we must also shift (but not print of course) the old array so that the indexes remain updated relative to one another. Then the other lines can just shift(old) or shift(new) accordingly. That must be why that one line was shifting twice. Here's Khen's augmented code:
      #!/usr/bin/perl use strict; use warnings; use CGI qw(:standard :html3); use Algorithm::Diff qw(traverse_sequences); use Text::Tabs; open(OUTPUT, ">out.html"); my $old = '1 2 3 4 6 7 8'; my $new = '1 2 5 3 6 4 7 8'; my @old = split( /\s+/, $old ); my @new = split( /\s+/, $new ); my $style = <<EOS; PRE { margin-left: 24pt; font-size: 12pt; font-family: Courier, monospaced; white-space:pre } PRE.onlynew { color: red } PRE.onlyold { color: blue } EOS print OUTPUT start_html( { -title => "$old vs $new", -style => { -code => $style } } ), h1( { -style => 'margin-left: 24pt' }, span( { -style => 'color: red' }, $old ), span(" <i>vs.<i> "), span( { -style => 'color: blue' }, $new ) ), "\n"; traverse_sequences( \@old, \@new, { MATCH => \&match, DISCARD_A => \&only_old, DISCARD_B => \&only_new, } ); print OUTPUT end_html; sub match { print OUTPUT shift(@old), "\n"; shift(@new); } sub only_old { print OUTPUT "<strong><font color=red>" . shift(@old) . "</font></ +strong>\n"; } sub only_new { print OUTPUT "<strong><font color=blue>" . shift(@new) . "</font>< +/strong>\n"; } close(OUTPUT);
      The code above should create an HTML document that prints the new array; black font means nothing changed at that spot b/t old and new. blue font means that character was added in new at that spot, and red font means new does not have that character at that spot. Thanks again for everyone's generosity!
Re: Traverse Sequences in Algorithm::Diff module
by tye (Sage) on Apr 21, 2011 at 20:10 UTC

    traverse_sequences()?? What a completely horrid interface! Just use the OO interface instead.

    - tye