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

I was working on something where I wanted to swap letter-pairs, each letter pair in a string, and I came up with this, which works:
$string = "thisisatest"; for($x=0;$x<(length($string)-1);$x++){ $temp = $string; substr($temp,$x,1) = substr($string,($x+1),1); substr($temp,($x+1),1) = substr($string,$x,1); print $temp, "\n"; }

Output:
htisisatest tihsisatest thsiisatest thiissatest thissiatest thisiastest thisistaest thisisaetst thisisatset thisisatets

But I was just wondering if there's a more Perlish way to do it that's not occurring to me?
--
($_='jjjuuusssttt annootthheer pppeeerrrlll haaaccckkeer')=~y/a-z//s;print;

Replies are listed 'Best First'.
Re: Transposition of each letter pair in string
by sauoq (Abbot) on Dec 27, 2002 at 06:34 UTC
    But I was just wondering if there's a more Perlish way to do it that's not occurring to me?

    This is one way to do exactly the same thing you are. I think it is a bit more "Perlish."

    my $string = 'thisisatest'; for my $i (0 .. length($string)-2) { (my $tmp = $string) =~ s/(.{$i})(.)(.)/$1$3$2/; print $tmp, "\n"; }
    -sauoq
    "My two cents aren't worth a dime.";
    
Re: Transposition of each letter pair in string
by BrowserUk (Patriarch) on Dec 27, 2002 at 09:04 UTC

    As a one-liner...

    $string = 'thisIsATest' (substr( $a = $string, $_, 2)) =~ s/(.)(.)/$2$1/ , print $a . $/ for (0 .. length($string) - 2) htisIsATest tihsIsATest thsiIsATest thiIssATest thissIATest thisIAsTest thisIsTAest thisIsAeTst thisIsATset thisIsATets

    Examine what is said, not who speaks.

Re: Transposition of each letter pair in string (one way)
by tye (Sage) on Dec 27, 2002 at 07:14 UTC
    #!/usr/bin/perl -w use strict; $_= "thisisatest"; print "$_:\n"; while( /(.)(?=(.))/g ) { print substr( $_, 0, pos($_)-1 ), $2, $1, substr( $_, pos($_)+1 ), $/; }

    Rewording the description in a manner that helped at least one person understand: Take a string, pick any pair of consecutive letters in the string (at random), swap them. What is the list of all possible results?

                    - tye
Re: Transposition of each letter pair in string
by Zaxo (Archbishop) on Dec 27, 2002 at 03:47 UTC

    Here's a Perl regexical way: s/(.)(.)/$2$1/g;

    Update: If you must exchange them one by one to percolate the first character over to the last, I won't help. I'd just say: s/(.)(.*)/$2$1/; That's fairly close to my original.

    Update2: What you say doesn't describe what you have. Nevermind, I'll let the psychics figure it out.

    After Compline,
    Zaxo

      That doesn't match his example very well. I would think it was a fine solution except he said his example works. His example seems to return (or print really) a list consisting of every possible reversal of a single pair of neighboring letters. I.e. transposition_list('foobar') would be the list ('ofobar', 'foobar', 'foboar', 'foobra'). I'm not sure how to handle double letters like the 'oo' in 'foo' but his doesn't handle them specially.

      I don't know if I really would do it this way but here is my untested first crack at it:

      print "$_\n" for transposition_list('thisisatest'); sub transposition_list { my $string = shift; my @transpositions; for my $i (0 .. length($string)-2) { (my $tmp = $string) =~ s/(.{$i})(.)(.)/$1$3$2/; push @transpositions, $tmp; } return @transpositions; }
      -sauoq
      "My two cents aren't worth a dime.";
      
Re: Transposition of each letter pair in string
by John M. Dlugosz (Monsignor) on Dec 27, 2002 at 04:50 UTC
    I think Zaxo's code is not doing the same thing. He is reversing each pair, and you are moving the first letter through each position in turn, and using each intermediate step. There is a nice Perlish way to find in a loop and process each in turn, but it doesn't work with s///. You might be able to simplify your code a little, but trying to get a regex to do it would be more obscure than what you have now.
Re: Transposition of each letter pair in string
by gjb (Vicar) on Dec 27, 2002 at 11:58 UTC

    A bit nasty, since I'm fiddling with the pos, but it does the trick without modifying the original string and temp variables:

    my $str = "thisisatest"; while ($str =~ /(.)(.)/g) { print "$`$2$1$'\n"; pos($str)--; }

    Just my 2 cents, -gjb-

Re: Transposition of each letter pair in string
by Aristotle (Chancellor) on Dec 27, 2002 at 23:54 UTC
    I'm surprised noone mentioned reverse. Here's a slightly insidious solution.
    use strict; use warnings; use Test::More q(no_plan); my $string = "thisisatest"; for my $i (0 .. (length($string) - 2)) { my @t = ($string)x2; # original substr($t[0], $i, 1) = substr($string, $i + 1, 1); substr($t[0], $i + 1, 1) = substr($string, $i, 1); # mine $_ = reverse $_ for substr($t[1], $i, 2); is($t[0], $t[1], "$t[0] eq $t[1]"); } __END__ ok 1 - htisisatest eq htisisatest ok 2 - tihsisatest eq tihsisatest ok 3 - thsiisatest eq thsiisatest ok 4 - thiissatest eq thiissatest ok 5 - thissiatest eq thissiatest ok 6 - thisiastest eq thisiastest ok 7 - thisistaest eq thisistaest ok 8 - thisisaetst eq thisisaetst ok 9 - thisisatset eq thisisatset ok 10 - thisisatets eq thisisatets 1..10

    It relies on the fact that for aliases rather than copies its variable - and substr returns an Lvalue you can assign to, so that snippet is the equivalent of the more verbose substr($t[1],$i,2) = reverse substr($t[1],$i,2);.

    That is, in my opinion, the most Perlish solution.

    Makeshifts last the longest.

Re: Transposition of each letter pair in string
by pg (Canon) on Dec 27, 2002 at 04:37 UTC
    There is a big discussion going on in the chat room, about this thread, as there are several different interpretations of what the author is actually trying to do.

    As for whether there is a more perlier way, I would say, any solution that solves the problem, looks normal, easy to understand would be a good one.

      Any way, now we got both cases covered, and hope there is no third one.

      I think there is a third one. The way his code works makes it clear. He wants not just one string back but a list of them. Each string is exactly the same as the original with exactly one letter pair reversed. Using your example of

      "01234567"
      the list would be
      ('10234567', '02134567', '01324567', '01243567', '01235467', '01234657', '01234576')
      Notice that the list will always contain one less element than the number of characters in the original string.

      I agree that his English explanation would seem to suggest Zaxo's solution. His code, however, suggests the above explanation.

      -sauoq
      "My two cents aren't worth a dime.";
      

      Code is more precise than English. If he says the code works, the safe assumption is that it actually does work the way he expects and his English description was lacking, not the other way around.

      -sauoq
      "My two cents aren't worth a dime.";
      
Re: Transposition of each letter pair in string
by Juerd (Abbot) on Dec 29, 2002 at 03:31 UTC

    $\ = "\n"; my $foo = 'thisisatest'; print for map { substr((my $bar = $foo), $_, 2, reverse substr $foo, $_, 2); $bar; } 0 .. -2 + length $foo;

    - Yes, I reinvent wheels.
    - Spam: Visit eurotraQ.
    

      ++, but why the complicated map games? :)
      my $foo = 'thisisatest'; for(0 .. -2 + length $foo) { substr((my $bar = $foo), $_, 2, reverse substr $foo, $_, 2); print $bar, $/; }

      Makeshifts last the longest.

        ++, but why the complicated map games? :)

        I had it without "for" first. print takes a list, map creates one. It made sense. But it all was printed on one line, so I added -l and for to make it print every item on its own line.

        When it functioned correctly, I pasted it in my web browser, and formatted it. Whenever you see me write $\ = "\n"; here, it's safe to assume I used only the command line to code and test, and used -l there.

        If I were to rewrite it, I'd probably have map generate strings that include \n as follows:

        my $foo = 'thisisatest'; print map { substr((my $bar = $foo), $_, 2, reverse substr $foo, $_, 2); "$bar\n"; } 0 .. -2 + length $foo;

        map feels natural to me. I use it a lot, and love it.

        - Yes, I reinvent wheels.
        - Spam: Visit eurotraQ.
        

Re: Transposition of each letter pair in string
by grexman (Beadle) on Dec 27, 2002 at 16:20 UTC
    Hello, Here is my suggestions..
    $string = "thisisatest"; @a=split //,$string; foreach (0..(@a-2)) { ($a[$_],$a[$_+1])=($a[$_+1],$a[$_]); print @a."\n"; ($a[$_],$a[$_+1])=($a[$_+1],$a[$_]); }
    Ciao, Gregor (Edit: forgot newline)
Re: Transposition of each letter pair in string
by Cody Pendant (Prior) on Dec 27, 2002 at 22:51 UTC
    Sorry about any confusion I may have caused.

    My code above did work, but I felt it was clunky and wondered about other approaches, that was all.

    For the record, the task was, as achieved above, to produce the array of results generated when you transpose each consecutive pair of adjacent letters: "test" becomes "etst, tset, tets" as first the t and e are transposed, then the e and s, then the s and t -- and the results were just fine. It was just the approach that I wanted you guys to comment on or show me alternatives to.
    --

    ($_='jjjuuusssttt annootthheer pppeeerrrlll haaaccckkeer')=~y/a-z//s;print;
Re: Transposition of each letter pair in string
by pizza_milkshake (Monk) on Dec 29, 2002 at 03:18 UTC
    perl -le'$s="thisisatest";map{$t=$s;(substr($t,$_,1),substr($t,$_+1,1) +)=(substr($t,$_+1,1),substr($t,$_,1));print $t}0..length($s)-2'
    perl -e'$_=q#: 13_2: 12/"{>: 8_4) (_4: 6/2"-2; 3;-2"\2: 5/7\_/\7: 12m m::#;s#:#\n#g;s#(\D)(\d+)#$1x$2#ge;print'
      map in void context? For shame. I don't think the original poster was asking for a golfed solution either. Other than that, the following (substr($t,$_,1),substr($t,$_+1,1))=(substr($t,$_+1,1),substr($t,$_,1)); is equivalent to but harder to read than substr($t, $_, 2) = reverse substr($t, $_, 2); From there it's any easy step to $_ = reverse $_ for substr($t, $_, 2);

      Makeshifts last the longest.