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

How do I reverse the order of the first and last word of a string? (e.g., The string: "This is my program" would be "program is my This")
  • Comment on How do I reverse the order of the first and last word of a string?

Replies are listed 'Best First'.
Re: How do I reverse the order of the first and last word of a string?
by GrandFather (Saint) on Mar 10, 2007 at 21:16 UTC

    I quite like this solution to the problem as an interesting technique for impressing teachers:

    use strict; use warnings; my $str = "This is my program"; my @words = split ' ', $str; $words[0] ^= $words[-1]; $words[-1] ^= $words[0]; $words[0] ^= $words[-1]; map {s/[^a-z ]//gi} @words; print join ' ', @words;

    Prints:

    program is my This

    DWIM is Perl's answer to Gödel
      Alternatively
      @$_[ 0, -1] = @$_[ -1, 0] and print "@$_\n" for [ split ' ', $str];
      Anno
      I quite like this solution to the problem as an interesting technique for impressing teachers:

      ++ for the funny reply, just as much as Anno's one. However...

      my $str = "This is my program"; my @words = split ' ', $str;

      [Still joking] This solution degrades whitespace, which his teacher may not want, and would earn him a bad mark. I'm astonished that no one took this into account yet. However the solution is easy:

      my @chunks = split /(\s+)/, $str;

      I changed @words to @chunks because they're not "words" any more. Of course later you may want to join on '' rather than on ' '.

      But... Oh no! If there's leading or trailing whitespace, then this solution will be broken. And I don't want it to earn him a bad mark. Thus the first thing to do is to get rid of the empty leading field:

      shift @chunks if $chunks[0] eq ''; # and then my $s = $chunks[0] =~ /\s/ ? 1 : 0; my $e = $chunks[-1] =~ /\s/ ? -2 : -1;

      Later, you'd use $s and $e instead of 0 and -1. Of course, since the code defining $s and $e is very similar, it may be factored away as to define both of them at the same time by means of map:

      my ($s,$e)=map $_ + ($chunks[$_] =~ /\s/ ? ($_ >=0 ? 1 : -1 ) : 0) => 0, -1;

      We still want to earn him some extra marks, don't we?

      map {s/[^a-z ]//gi} @words;

      [On a more serious basis] I know that the tone of your answer is being sarcastic, but it also spawned some interest because of the notorious XOR technique, and up until now it has been a funny way to do something that one would better do differently. But it was also fundamentally correct, i.e. it exposed no Perl bad programming habit, while the OP is clearly a newbie and learning. So now what you show him is a use of map in void context for its side effects only, with a s used as a tr. If really wanting to use map, (and not having yet a .subst()) that should be

      @words = map { (my $s=$_) =~ s/[^a-z ]//gi; $s } @words;

      Moreover, the only spurious charachter will be a "\0" so with a

      y/\0//d for @chunks; # or @chunks[$e,$s];

      we would lose some irony but do a better instructive job.

      Back to the joke! What could we do to earn the OP some more marks? Well, there are too many "chunks" around, and we may use the same trick as Anno's to factor them away. And as someone suggested, use interpolation in a double quoted string. The teacher will certainly be happy:

      for ([split /(\s+)/, $str]) { shift @$_ if $_->[0] eq ''; my ($s,$e)= do { my @c=@$_; map $_ + ($c[$_] =~ /\s/ ? ($_ >=0 ? 1 : -1 ) : 0) => 0, -1; }; $_->[$s] ^= $_->[$e]; $_->[$e] ^= $_->[$s]; $_->[$s] ^= $_->[$e]; y/\0//d for @$_; local $"=''; print "@$_"; }
        my @chunks = split /(\s+)/, $str;

        I prefer not to throw anything away in such cases:

        my @chunk = split /(?<=\b)(?=\w)/, $str;

        Of course this complicates the code for reversing a word, as you have to keep the trailing whitespace at the end (which can be accomplished with sort if you're careful), but it also means that if the original has two spaces after a word, your final result will too!

        -- 
        We're working on a six-year set of freely redistributable Vacation Bible School materials.
      I appreciate the help as I am new to Perl and programming in general.

        Welcome to the Monastery. Generally you will find that you get the most help when you show that you have done some work and demonstrate that you are not just looking for someone else to do your homework for you.

        I suspect that if you hand in the code I gave above as a homework answer your teacher will either not believe that you wrote it, or will think that you are a smart arse and shouldn't be in the course.

        However if you read the documentation for split and join you may find that you can come up with a more acceptable solution for yourself.


        DWIM is Perl's answer to Gödel
      ok so trying to accomplish this task differently and on my own I am stuck once again. here is what I have.. $_ = "this is my program to reverse"; s/(\w+) (\w+)/$2 $1/; print; obviously this only swaps the first two words. I cannot figure out how to swap the first and last word this way. Is it even possible? I literally spent most of the day working on a program and this is the only thing I am stuck on. Please help.
        That's a respectable attempt you've made. Like you said, the problem is that you're only swapping the first and the second word. You should also try to match the stuff between two words, and suddenly it's not longer impossible to match the first and the last word.

        If you try

        s/(\w+)(.*)(\w+)/$3$2$1/
        you'll see that you'll have some trouble with greediness of the middle match: the "last word" will match too little: I expect it to match just the last letter.
        s/(\w+)(.*?)(\w+)/$3$2$1/
        is the other extreme, and you'll match too little.

        I can think of two solutions:

        1. Use the (latter) non-greedy match, but anchor that far side to the end of the string:
          s/(\w+)(.*?)(\w+)$/$3$2$1/
          or
          s/(\w+)(.*?)(\w+)(\W*)$/$3$2$1$4/
        2. Require there's a word boundary just in front of the last match, and match greedily.
          s/(\w+)(.*)\b(\w+)$/$3$2$1/
        All untested, but I have faith. :)
      And thse people will wonder where suddenly the null bytes are coming from. There'll be exactly as many as the difference in length between the first and the last word, because the shortest word will be padded with "\0" to the length of the longest one.

        Actually the void context map that gives blazar goose bumps below fixes that problem. As blazar suggests, a translate (y/\0//d for @words;) would be cleaner however.


        DWIM is Perl's answer to Gödel
Re: How do I reverse the order of the first and last word of a string?
by merlyn (Sage) on Mar 10, 2007 at 20:51 UTC
Re: How do I reverse the order of the first and last word of a string?
by Joost (Canon) on Mar 10, 2007 at 20:58 UTC
Re: How do I reverse the order of the first and last word of a string?
by jwkrahn (Abbot) on Mar 11, 2007 at 05:25 UTC
    $ perl -le' $_ = "This is my program"; print reverse /^(\S+)(.*?)(\S+)$/ ' program is my This
Re: How do I reverse the order of the first and last word of a string?
by Zaxo (Archbishop) on Mar 11, 2007 at 06:34 UTC

    Pretty directly,

    my $string = 'This is my program'; print do { my @arr = split ' ', $string; join ' ', @arr[-1, 1..$#arr-1, 0],$/ };
    or,
    my $string = 'This is my program'; { my @arr = split ' ',$string; print "@arr[-1, 1..$#arr-1, 0]$/" }
    That code uses array slices.

    After Compline,
    Zaxo

      and doing without the join:

      my $string = 'This is my program'; my @arr = split ' ', $string; print "@arr[-1, 1..$#arr-1, 0]";

      After all, we don't want to introduce too much on the first lesson.


      DWIM is Perl's answer to Gödel

        I think I added the similar code while you were writing that. In any case, join is probably easier to teach than $", the array interpolation seperator.

        After Compline,
        Zaxo