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

H,

This is my function to swap each two character of the input data. As example, the input data is 12345678 and the output will be 21436587. Is there are others solution do you have ?

sub swap { my ($a, $b); my @tmp; my $in = shift; for (my $i=0; $i < length($in)-1; $i+=2) { $a = substr($in,$i,1); $b = substr($in,$i+1,1); push(@tmp, $b . $a); } $out = sprintf ("@tmp"); $out =~ s/\s//g; return $out; }

Edited (davorg): removed code tags from non-code sections

Replies are listed 'Best First'.
Re: What is the best solution to swap input data?
by GrandFather (Saint) on Nov 09, 2006 at 06:58 UTC

    Using a regex is a little more compact:

    use strict; use warnings; my $str = '12345678'; $str =~ s/(.)(.)/$2$1/g; print $str;

    Prints:

    21436587

    Update: BTW, using $a and $b as variables is a bad idea. They are the variables used by sort.


    DWIM is Perl's answer to Gödel
Re: What is the best solution to swap input data?
by ikegami (Patriarch) on Nov 09, 2006 at 07:28 UTC

    GrandFather nailed it, so this isn't an answer to your question, but a comment on

    $out = sprintf("@tmp");

    It's wrong. Using sprintf will cause any % to be interpreted specially.

    local $" = ''; # Don't insert spaces. $out = "@tmp"; # sprintf not needed at all.

    A better solution would be:

    $out = join('', @tmp);

    I've also noticed you've been putting your comments in <code>/<c> tags. Please don't do that. Only put code (and other pre-formatted data) in <code>/<c> tags. If the problem is that you don't know HTML, pretty much all you need to know is to put <p> at the start of every paragraph.

Re: What is the best solution to swap input data?
by davido (Cardinal) on Nov 09, 2006 at 07:41 UTC

    GrandFather's solution is the most appropriate one I can think of, but there's something about (.)(.) that bugs me. ...no, not for any good reason. ;)

    So here's an inferior solution that doesn't annoy me with (.)(.) (those beady eyes).

    my $data = '123456789'; $data =~ s/(.{2})/reverse $1/eg; print $data, "\n";

    You didn't define what happens if the string has an uneven number of characters. The most likely alternative is to do nothing with that extra digit at the end. ...but maybe you would prefer doing nothing with the leading digit, in the case of an uneven number of digits, or maybe you'd prefer to fail altogether. ...just some things to consider...

    In case you're wondering why this solution is inferior, here's a small list:

    • reverse $1 is more verbose than $2$1
    • s///eg performs a string eval, which is less efficient than $2$1, and more prone to programmer error.
    • (.{2}) is possibly harder to read than (.)(.), even if it's less annoying looking.
    • In case you haven't guessed, this whole commentary should be taken tongue in cheek.

    Update:
    The OP's original solution used substr, and since I was in the mood to tinker, I decided to work up my own substr version:

    use strict; use warnings; my $data = '123456789'; my $output = ''; my $position = 0; while ( $position < length $data ) { $output .= reverse substr $data, $position, 2; $position += 2; } print $output, "\n";

    Dave

      Here's a substr solution that works in situ:
      my $data = '123456789'; foreach (my $i = 0; $i + 1 < length $data; $i += 2) { substr($data, $i, 2) = reverse substr($data, $i, 2); } print $data, "\n"; __END__ 214365879
        That's fenLisesi's solution here in this thread, only using an assignment instead of the 4-arg form of substr.

        -- Hofmator

        Code written by Hofmator and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: What is the best solution to swap input data?
by fenLisesi (Priest) on Nov 09, 2006 at 07:52 UTC
    A few minor points that caught my attention:
    • $a and $b are not the best variable names, as they are special
    • You could use the replacement capability of substr to eliminate some temp vars
    • In general, it is a good idea to unload args to a function as early as possible, so the shift statement would better become the first line
    • It is a good idea to limit the scope of your vars as much as possible, so my ($a, $b) would be closer to home inside the for loop
    sub swap { my ($str) = @_; die 'No input?' unless defined $str; ## 0 to half-length minus one for my $i (0 .. (length( $str ) >> 1) - 1) { my $j = $i << 1; substr( $str, $j, 2, reverse substr( $str, $j, 2 ) ); } return $str; }
    Cheers.

    Update: Took sub swap {...} out of readmore

Re: What is the best solution to swap input data?
by johngg (Canon) on Nov 09, 2006 at 11:39 UTC
    I think that GrandFather's regular expression solution is the best for you but you ask what other solutions we have. davido has come up with an improved substr solution. Here's a way with split, splice and join.

    use strict; use warnings; my $str = q{12345678}; print qq{$str\n}; $str = revByGroup($str); print qq{$str\n}; sub revByGroup { my $str = shift; my @chars = split m{}, $str; for (my $idx = 0; $idx < $#chars; $idx += 2) { splice @chars, $idx, 0, splice @chars, $idx + 1, 1; } return join q{}, @chars; }

    and the output is

    12345678 21436587

    It copes with an odd numer of characters, leaving the last odd character at the end of the string. I hope this is of interest.

    Cheers,

    JohnGG

Re: What is the best solution to swap input data?
by swampyankee (Parson) on Nov 09, 2006 at 15:27 UTC

    You can also do this with split, although my implementation is not quite optimal.

    #!/usr/bin/perl use strict; use warnings; while(<DATA>){ chomp; my $in = $_; my @pairs = split(/(.{2})/); $_ = reverse foreach @pairs; my $out = join('', @pairs); print "in: $in => out: $out\n"; } __DATA__ 12345678 1234 12 1234 123456 87654321

    Outputs:

    in: 12345678 => out: 21436587 in: 1234 => out: 2143 in: 12 => out: 21 in: 1234 => out: 2143 in: 123456 => out: 214365 in: 87654321 => out: 78563412

    As I said, I'm quite sure that this is far from optimal, as the regex is probably quicker, shorter, and at least as clear.

    emc

    At that time [1909] the chief engineer was almost always the chief test pilot as well. That had the fortunate result of eliminating poor engineering early in aviation.

    —Igor Sikorsky, reported in AOPA Pilot magazine February 2003.
      You are right, the regex is quicker but not the quickest. That honour goes davido's and fenLisesi's substr methods which are neck and neck. My split, splice and join gets the wooden spoon, being desperately slower than any other method.

      I had also come up with almost the same method as your's but using a map rather than storing in an array. Your version is slightly the faster. One way of speeding both up is to grep out the empty elements that result from the split. The benchmark code first works on a short odd-length string to check that all methods produce the same result. Then methods are compared with a longer string. Here's the results

      reRev - 214365879 reSwap - 214365879 spliceSwap - 214365879 splitMap - 214365879 splitMapGrep - 214365879 splitRev - 214365879 splitRevGrep - 214365879 substrRev - 214365879 substrSubstr - 214365879 twoSubstrs - 214365879 Rate SpliceSwap SplitMap SplitRev TwoSubstrs SplitMapGr +ep SplitRevGrep ReRev ReSwap SubstrRev SubstrSubstr SpliceSwap 4.15/s -- -64% -68% -70% -7 +1% -72% -84% -85% -89% -89% SplitMap 11.7/s 181% -- -9% -17% -1 +9% -22% -54% -59% -68% -68% SplitRev 12.8/s 209% 10% -- -8% -1 +1% -14% -50% -55% -65% -65% TwoSubstrs 14.0/s 237% 20% 9% -- - +3% -7% -45% -50% -62% -62% SplitMapGrep 14.4/s 247% 23% 13% 3% +-- -4% -44% -49% -61% -61% SplitRevGrep 15.0/s 261% 28% 17% 7% +4% -- -42% -47% -59% -59% ReRev 25.6/s 517% 119% 100% 83% 7 +8% 71% -- -9% -30% -30% ReSwap 28.3/s 580% 142% 120% 102% 9 +6% 89% 10% -- -23% -23% SubstrRev 36.7/s 785% 214% 187% 162% 15 +5% 145% 43% 30% -- -0% SubstrSubstr 36.9/s 788% 215% 188% 163% 15 +6% 146% 44% 30% 0% --

      and here's the code

      I hope this is of interest.

      Cheers,

      JohnGG

        For thorough evaluation of alternatives, johngg++

        emc

        At that time [1909] the chief engineer was almost always the chief test pilot as well. That had the fortunate result of eliminating poor engineering early in aviation.

        —Igor Sikorsky, reported in AOPA Pilot magazine February 2003.
A reply falls below the community's threshold of quality. You may see it by logging in.