http://qs321.pair.com?node_id=255669

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

In a recent discussion, a friend told me 'she was glad I was glad', to which I (of course) immediately replied that 'I was glad she was glad I was glad'. One thing led to another (well, not that....) and voila:

#! /usr/bin/perl use strict; my $limit = $ARGV[0] || 10; my $index = 0; my $result = "I am glad."; $result = ( "I am", "you are" )[ $index = not $index ] . " glad " . $result foreach ( 1..$limit ); print $result, "\n";

This thing takes an integer from the command line and writes a (possibly ridiculously long) string saying 'I am glad you are glad I am glad...' eg.

c:/Data $ perl fn.pl 20 I am glad you are glad I am glad you are glad I am glad you are glad I + am glad you are glad I am glad you are glad I am glad you are glad I + am glad you are glad I am glad you are glad I am glad you are glad I + am glad you are glad I am glad.

The point here is the little list that toggles between 'I am' and 'you are'. The above snippet solves this nicely, but I find it noisy. Having read something about closures recently I then came up with the following:

#! /usr/bin/perl use strict; my $limit = $ARGV[0] || 10; my $result = "jeg er glad."; while( my $head = &swap ) { $result = $head. " er glad " . $result; } print $result, "\n"; BEGIN { my @oss = qw( jeg du ); my ( $index, $count ); sub swap{ ( $limit > $count++ ) ? return $oss[ $index = not $index ] : return; } }

The code does the same (although the result is in norwegian), and I like this better as it results in only one word where I want the toggle. As a downside, the code's "framework" is extremely verbose.

How does one toggle between two values in a simple and elegant way? Not too long-winded? Not too obscure?

As a bonus question, why doesn't ...

$result = $head. " er glad " . $result while( my $head = &swap );

... work under strict? It works with $head predeclared, but I'd rather not as one-liners are more neat. It seems to me that a statement like the above would define $head in the preceding line. Does a my statement inside a condition only apply to the following block? Not the logical "preceding block" to which the conditional is linked?

As a side note, I forgot why I named the variable $head. Now it makes sense, though, if only I knew why.

Cheers!

Replies are listed 'Best First'.
•Re: Toggling between two values
by merlyn (Sage) on May 05, 2003 at 17:22 UTC
    use Tie::Cycle; # in the CPAN tie my $cycle, 'Tie::Cycle', [ qw( FFFFFF 000000 FFFF00 ) ]; print $cycle; # FFFFFF print $cycle; # 000000 print $cycle; # FFFF00 print $cycle; # FFFFFF back to the beginning (tied $cycle)->reset; # back to the beginning

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: Toggling between two values
by chip (Curate) on May 05, 2003 at 17:22 UTC
    Your "closure" sub isn't actually a closure; to be a closure it would not only have to access outside lexicals (like yours does), but also be anonymous. What you have works anyway because you don't attempt to have several independent swap subs each of which has its own private state.

    BTW, the reason why this doesn't work: $result =  $head. " er glad " . $result while( my $head = &swap ); ... is that the parsing works left-to-right, and so $head has to be declared by the point where it is first seen in scanning, not where it would first be touched in execution.

        -- Chip Salzenberg, Free-Floating Agent of Chaos

      Your "closure" sub isn't actually a closure; to be a closure it would not only have to access outside lexicals (like yours does), but also be anonymous.
      No, "closure" and "named" are orthogonal properties. In the following, the named subroutine gimme is a closure:
      BEGIN { my $number = 0; sub gimme { ++$number; } }
      Yes, there's some Perl literature that mixes up the two concepts of anonymous and closure because they are often correlated, but really they are separate properties.

      Although I do feel a bit weird correcting chip on this point. {grin}

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

        "closure" and "named" are orthogonal properties

        I'm afraid that's not quite true, at least in Perl. Perl subs that are named are not closures, by fiat. This choice was made to prevent imposing closure overhead on subroutines that don't need closure behavior (i.e. the capturing of values at time of sub reference).

        The rule, as best I recall, is: In Perl, a closure is an anonymous subroutine that accesses at least one lexical variable not declared at global scope. BTW, IIRC, BEGIN blocks are deemed to be global scope for the purpose of deciding whether a sub is a closure.

        I do feel a bit weird correcting chip on this point.

        Well, you should, since you're wrong. {grin}

            -- Chip Salzenberg, Free-Floating Agent of Chaos

Re: Toggling between two values
by Aristotle (Chancellor) on May 05, 2003 at 18:37 UTC
    #!/usr/bin/perl -w use strict; my $limit = $ARGV[0] || 10; my @cycle = ( "you are", "I am" ); print map "\u$_\n", join(" glad ", reverse map $cycle[$_ % @cycle], 1 +.. $limit) . " glad.";
    (It is funny how many pitfalls can await one even in such a simple piece of code.)

    Makeshifts last the longest.

Re: Toggling between two values
by boo_radley (Parson) on May 05, 2003 at 23:29 UTC
    untested, not near perl, but sufficiently bored.
    my %annoying; $annoying {"I am"}= "you are"; $annoying {"you are"} = "I am"; $key = "I am"; for (1..10) { print $annoying{$key}," glad "; $key= $annoying{$key}; } print ".";
Re: Toggling between two values
by Jenda (Abbot) on May 06, 2003 at 18:43 UTC

    Use the ?: operator instead of the list:

    perl -e "$_=shift();print($x++ % 2 ? 'you are glad ' : 'I am glad ')wh +ile($_--)" 4 or perl -e "print($x++ % 2 ? 'you are glad ' : 'I am glad ') foreach(1..s +hift())" 4

    Jenda
    Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
       -- Rick Osborne

    Edit by castaway: Closed small tag in signature

Re: Toggling between two values
by Nkuvu (Priest) on May 05, 2003 at 20:31 UTC

    Eh. I like the first one. Because my feeble Perl brain can understand it.

Re: Toggling between two values
by paulbort (Hermit) on May 06, 2003 at 17:46 UTC
    From the "Still Learning, but I have a Camel Book" department:

    Would the bi-stable operator (..) help here? I just read the description three times, and it sounds right, but I can't make the last step from what the book says (pp.103-104, 3rd Ed.) to code.

    Any ideas, or am I barking up the wrong operator?

    --
    Spring: Forces, Coiled Again!
      It won't work anymore than the other options. The reason is that the bi-stable operator doesn't reset itself. Cycles have to reset. :-)

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.