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

Hello, I am trying to learn perl at the moment, with the help of O'Reilly's Learning Perl (2nd ed). I am up to chapter 7 and I am having a bit of a hard time visualizing solutions to the exercises. Part of the problem is they throw the abreviated versions at you a bit too fast. I like seeing things spelled out the long way, so I can figure out exactly how the code is working. Take for example Ch 7 Ex 2. The brief answer is:
while (<STDIN>) { if (/a/i && /e/i && /i/i && /o/i && /u/i)) { print; } }
Clear enough, but to make sure I am following what is happening I tried writing it out the long way, and failed, telling me that I don't truly understand the concept of what is happening. Essentially I have this:
while ( defined ($line = <STDIN>) ) { if ($line =~ (/a/i && /e/i && /i/i && /o/i && /u/i)) { print $line; } }
I know it is something simple, but I can't put my finger on it... I'm sure everyone reading this sees how stupid I am being, so can someone let me in on the joke? Thanks.

Replies are listed 'Best First'.
Re: Long Versions
by Joost (Canon) on Sep 26, 2007 at 23:19 UTC
    I think you've got the meaning right, you're just mistaken about the syntax of && and //.

    The correct translation of the if() statement, using your $line variable would be

    if ($line =~ /a/i && $line =~ /e/i && $line =~ /i/i && $line =~ /o/i +&& $line =~ /u/i) {

    Where you're going wrong is that && does not combine matches into a single match, it just does a logical AND of the results of the matches.

    And any // match without a preceding $variable =~ statement matches on $_, the "implicit"/"default"/"this" variable.

    So your original translation boils down to *) - see update 2

    if ($line =~ /a/i && $_ =~ /e/i && $_ =~ /i/i && $_ =~ /o/i && $_ =~ +/u/i) {

    update: personally, I find the PP book's version much easier on the eyes, and it's definitely more readable once you're used to seeing // matches without explicit $variable =~ parts, but I agree it crams a lot of meaning into a very short line.

    The Camel book tends to be like that: it favours idiom over explicitness. IMHO the 3rd edition is a little gentler in this regard, especially in the beginning of the book.

    update 2: Sidhekin pointed out that your line actually does something else. Here is your line:

    $line =~ (/a/i && /e/i && /i/i && /o/i && /u/i)
    Note that you're trying to use the result of (/a/i && /e/i && /i/i && /o/i && /u/i) (which will probably 1 or undef) as a match on $line.

Re: Long Versions
by Cristoforo (Curate) on Sep 26, 2007 at 23:21 UTC
    Hello and welcome to the monastery!

    Part of the problem is they throw the abreviated versions at you a bit too fast. I like seeing things spelled out the long way, so I can figure out exactly how the code is working

    The beauty of Perl is that it does allow these *shortcuts* and in time, you will appreciate how much this cuts down on code clutter - not possible in languages generally.

    Reading posts here in Perl Monks will help accelerate the learning process. Often, I will attempt a solution to a question and then compare it to others' solutions - a good exercise.

    You'll pick it up in no time.

    Your code would have to be written::

    while ( defined ($line = <STDIN>) ) { if ($line =~ /a/i && $line =~ /e/i && $line =~ /i/i && $line =~ /o/ +i && $line =~ /u/i)) { print $line; } }
Re: Long Versions
by BoulderBum (Novice) on Sep 27, 2007 at 00:33 UTC
    Ahhh... thanks to you both. I understand where I went wrong now. Don't get me wrong either, I really appreciate the susinctness of Perl. However, when learning a new language sometimes it is detrimental to go right to the slang. Take the following Quebec phrases:
    vlą == voilą, asteur == en cette heure, chuis == Je suis.
    Had I started speaking french like that, I wouldn't know what I was really saying half the time and couldn't explain it to anyone else.
    Tango Mike (TM == Thanks Much in army slang)
Re: Long Versions
by cdarke (Prior) on Sep 27, 2007 at 09:59 UTC
    While you will find that the shortcuts come naturally with practice, if you are having trouble understanding code with hidden magic, try using the B::Deparse module, like this:

    $ perl -MO=Deparse script-name.pl

    The Ch 7 Ex 2 example (with syntax error corrected) gives:
    while (defined($_ = <STDIN>)) { if (/a/i and /e/i and /i/i and /o/i and /u/i) { print $_; } }
Re: Long Versions
by Aim9b (Monk) on Sep 27, 2007 at 22:36 UTC
    BoulderBum, be not dismayed... for I to am learning perl, and have honed stupidity to a fine art. The monestary here is a world of help. The 3rd edition of PP, while a little better than the 2nd, is also over 1000 pages, and my library wanted it back.

    My question is for the more seasoned responders. Is the example cited only expecting to print when ALL of the vowels are contained in the line, or when ANY vowel is found. At first I thought it was looking for ANY vowel, but the 'and' would imply they all must be matched. Or is it the FIRST one matched causes a print, unless the /g is present. Just testing my recollection...it's an age thing. Thanks.
Re: Long Versions
by BoulderBum (Novice) on Sep 28, 2007 at 00:38 UTC
    It is supposed to print the line when all of the vowels are present. The Deparse module seems like a good idea, except it missed the one expansion step that I was missing. I had this:
    $line =~ (/a/i && /e/i && /i/i && /o/i $$ /u/i)
    What i needed was:
    ($line =~ /a/i && $line =~ /e/i && $line =~ /i/i && $line =~ /o/i $$ $ +line =~ /u/i)
    The deparse did not show all of these individual matches with the built in $_ ($line in my example).