http://www.perlmonks.org?node_id=1004820

ok nothing Ammazzingg here really, just a little bit neat. (And what the heck 1 vote to go for next level Scribe :D)

looking at simple package / subroutines I wrote a sub that had a print operation as its last argument. I called the sub from within a print.

the starting problem was that my print statements were all jumbled up with the print from the sub being evaluated then the print itself and finally a return of 1 or true from the call to the sub at the end of the print.

package Htmlz; sub ah{ print 'an experiment' } package Effect; print 'im conducting ', Htmlz::ah; ------- an experimentim conducting1

:( right so i eventually realise pass a scalar back! duh!

package Htmlz; sub ah{ my $fizix = 'an experiment' } package Effect; print 'im conducting ', Htmlz::ah; -------- im conducting an experiment

:) hooray this is how it is supposed to be! but as I say I was grappling with what was happenning so I tried wantarray ? : to see if maybe the caller of the sub had been expecting an array at some point. And something interesting happened. The sub evaluation returned the print as in the first attempt. So it was truly expecting an array afer being called from a print. Or was it? However, as the last statement was now a scalar, instead of returning true or 1, the scalar itself was also returned during the evaluation of the print statement. So it was also expecting a scalar??

At which point I hold my hands up and start thinking 'is this what they mean by a side effect?' and post the code

package Htmlz; sub ah{ wantarray? my @givearr = print 'anexperiment' : my $fizix = ' kerpow!' } package Effect; print 'im conducting ', Htmlz::ah; ------- anexperimentim conducting kerpow!

Still printing out the wrong way round, but the 1 is replaced with kerpow. So what is intersting is, how about I do..

package Htmlz; sub ah{ my $p = my $htmlparagraphelement = shift; wantarray ? my @givearr = print '<',$p,'>' : my $fizix = '</'.$p.'>' } package Effect; print 'im conducting kerpow! an experiment', Htmlz::ah('p'); #pass p ------- <p>im conducting kerpow! an experiment</p>

I think this is why I like perl.

Unfortunately though, this doesnt nest too well when you start to add more elements. I do however seem to spend a lot of time previewing my posts here...


print 'Coyote',Htmlz::ah('H1');

Replies are listed 'Best First'.
Re: new way to code html?
by ColonelPanic (Friar) on Nov 21, 2012 at 14:29 UTC

    It took me forever to figure it out, but here is what is happening with your code. The ternary operator ( - ? - : - ) is not being interpreted as you think it is. If you add parentheses, the behavior goes away:

    #No longer "works" sub ah{ my $p = my $htmlparagraphelement = shift; ( wantarray ? (my @givearr = print '<',$p,'>') : (my $fizix = '</'.$p.'>') ) }

    Instead, what you wrote is being interpreted as this:

    #Same as the code without parentheses. sub ah{ my $p = my $htmlparagraphelement = shift; ( wantarray ? (my @givearr = print '<',$p,'>') : (my $fizix ) ) = '</'.$p.'>' }

    That's right, in Perl you can assign to a ternary operator expression! This "feature" allows you to assign to one of two different variables based on a conditional.

    So, what is happening?

    1. wantarray is always true when your sub is called.
    2. Thus, you execute your array assignment and print the opening tag statement.
    3. Then your closing assignment is applied to the result of the ternary operator. This overwrites @givearr with the closing tag value that you intended to go into $fizix.
    4. Because you have no return statement, the result of the last statement executed is returned. This happens to be the closing tag.

    The result: some truly bizarre behavior. It would make a great trick for some obfuscated code. Barring that, you should never, ever, ever (ever!) do it again.

    I do intend to ++ your post, though, because this was really fun.



    When's the last time you used duct tape on a duct? --Larry Wall

      Colonel, way to go! :)

      yah, once again precedence rears its ugly head and cuts us down at the knees.

      I lumped in wantarray with the ternary operator, to get what I thought was the wantarray operation... not so though hey. So that was what was happening. Glad you had fun working that out, I had fun constructing it.

      Pretty happy with the side effect heaven access level 1 granted card too ;)

      and yes i made scribe, next level MONK!!!!!

      votes all ++


      foreach(@{$Coyote->{thread}{users}}){wantvote ? ++ : -- } = ++ #whats happening here CP??? lol
Re: new way to code html?
by muba (Priest) on Nov 21, 2012 at 01:51 UTC

    ?: should really be used to let Perl choose one or another value based on the truthfullness of an expression, not as a flow control mechanism. If you want flow control, use if and else

    # Ok - pick a value based on the value of $bar $foo = $bar eq "baz" ? "foobaz" : "foobar"; print "I am the ", ($sex eq "male" ? "king" : "queen"), "of rock & rol +l"; some_subroutine( ref($foo) eq "ARRAY" ? $foo : [$foo] );
    # Not ok - ?: flow control $bar eq "baz" ? $foo = "foo" : $bar = "baz";
    # Ok - if/else flow control if ($bar eq "baz") { $foo = "foo"; } else { $bar = "baz"; }
    # Ok - if/else flow control, if a bit verbose. if ($bar eq "baz") { $foo = "foo"; } else { $foo = "baz"; }

      Using the ternary operator (?:) over if/else can sometimes be a useful optimisation. Especially if it's in a deeply nested loop that gets executed thousands of times.

      On my machine, ternary is consistently about 25% faster.

      use Benchmark ':all'; my ($foo, $bar); cmpthese(-1, { ternary => sub { int(rand(2)) ? ( $foo = 1 ) : ( $bar = 1 ) + }, if_else => sub { if(int(rand(2))) { $foo = 1 } else { $bar = 1 } + }, });

      Why is this? It's because the braces used in if/else structures introduce a new lexical scope, so when they're encountered Perl needs to create a new pad (i.e. a structure where it keeps my variables) even if that pad doesn't get used.

      Of course, generally speaking clarity should win over speed. But in subs which are executed many thousands of times, use of the ternary for simple conditionals is a handy trick to have up your sleeve.

      See also: the block forms of map and grep; control structures versus statement modifiers.

      Update: thanks anonymous monk below for pointing out the precedence error in my original code for the ternary sub. Corrected above now. I think this illustrates perfectly my statement that "generally speaking clarity should win over speed". :-)

      perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

        use of the ternary for simple conditionals is a handy trick to have up your sleeve.

        I'd worry about having a grasp of the language (context, syntax) before worrying about micro-optimizations

        -MO=Deparse,-p says

        use Benchmark (':all'); my($foo, $bar); cmpthese((-1), {'ternary', sub { ((int(rand(2)) ? ($foo = 1) : $bar) = 1); } , 'if_else', sub { if (int(rand(2))) { ($foo = 1); } else { ($bar = 1); } } });

        More clearly  ((int(rand(2)) ? ($foo = 1) : $bar) = 1);

        is

        ( ( int(rand(2)) ? ($foo = 1) : $bar ) = 1 );
        which is an error
Re: new way to code html?
by AnomalousMonk (Archbishop) on Nov 21, 2012 at 00:30 UTC

    Even after fixing all the syntax errors, I can't get your third and fourth code examples to produce the output you show. Is  @givearr tie-ed in some way? Can you please post complete, self-contained code, along with its output?

    And yes, you have entered Side-Effect Heaven!

      Anomylous & others, yes sorry, the '@givearr =' assignment should have print as a rvalue.. and semi-colon after package declarations. I have updated, now the 3rd/4th example should work.

Re: new way to code html? (no, syntax errors never are)
by Anonymous Monk on Nov 20, 2012 at 23:43 UTC

    no, syntax errors never are

    use return, use ;;;;;