http://qs1969.pair.com?node_id=1079916

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

Writing a program to rename copied photos throughout several folders I am using several greps, at one stage I needed to filter a folder, rather than add another layer of grep I placed an if conditional inside an anonymous list constructor, and discovered that the ?: tertiary conditional may not actually be a synonym for if.

There is most likely a simple reason why the if statement(term/expression?) produces a syntax error if used inside a refto/list constructor, but I wondered if this was due to greater reason than being condusive to writing better code? (as in, hint: use if to control flow not determine variables)

from my examples here, I summise that the tertiary conditional is an if statement(term/expression), wrapped in a do block. Is this the case?

my @arr = ( if(1<2){'1'}else{()} ); #syntax error my $arrref = \( if(1<2){'1'}else{()} ); #syntax error my $arrref2 = [ 1<2 ? '1' : () ]; #works my @arr2 = ( do { if(1<2){'1'}else{()} } ); #works if(1<2){print '1',$/ }else{print '2',$/}; #works 1<2 ? print '1',$/ : print '2',$/; #works ( 1<2 ? print '1',$/ : print '2',$/ ); #works ( if(1<2){print '1',$/ }else{print '2',$/} ); #syntax error

Replies are listed 'Best First'.
Re: conditional statements in list constructors
by AnomalousMonk (Archbishop) on Mar 27, 2014 at 14:05 UTC
    ... the tertiary conditional is an if statement(term/expression), wrapped in a do block.

    The "tertiary conditional" is a conditional expression: it evaluates to one thing or else another thing. It may produce an effect like that of a  do { ... } block, but I would say it is incorrect to say it is a do-block. (Update: E.g., the  + operator produces a result like that of a do-block with some arithmetic operations in it, but it makes no sense to say it is a do-block.) What does the decompiler say?

    Update: Added example. (Update: Changed example to include do-block in list.)

    c:\@Work\Perl\monks>perl -wMstrict -le "my $x = 1; ;; my @ra = ( 'x', $x ? 'foo' : 'bar', 'y', do { if ($x) { 'biz' } else { 'boz' } } ); print qq{@ra}; " x foo y biz c:\@Work\Perl\monks>perl -wMstrict -MO=Deparse,-p -le "my $x = 1; ;; my @ra = ( 'x', $x ? 'foo' : 'bar', 'y', do { if ($x) { 'biz' } else { 'boz' } } ); print qq{@ra}; " BEGIN { $^W = 1; } BEGIN { $/ = "\n"; $\ = "\n"; } use strict 'refs'; (my $x = 1); (my(@ra) = ('x', ($x ? 'foo' : 'bar'), 'y', do { if ($x) { 'biz'; } else { 'boz'; } })); print("@ra"); -e syntax OK
Re: conditional statements in list constructors
by SuicideJunkie (Vicar) on Mar 27, 2014 at 14:59 UTC

    If you want to conditionally include things in a list, try the x!! operator combo:

    my $pi = 0; my $life = 'isgood'; my $foo = 1; my $aref = [ (3,1,4) x !!($pi), (42) x !!($life), ('bar', 'baz') x !!( +$foo) ]; print Dumper $aref;
    gives:
    $VAR1 = [ 42, 'bar', 'baz' ];
Re: conditional statements in list constructors
by kcott (Archbishop) on Mar 27, 2014 at 14:48 UTC

    G'day Don Coyote,

    if may be used as a Compound Statement or as a Statement Modifier. It is not an operator.

    Placing an if statement in a do block, as you show, can emulate the ternary operator but, as is fairly obvious, results in clunky code with poor readability and maintainability.

    So, if introduces a compound statement (or statement modifier) and ?: is an operator. They are not synonymous. With do blocks, parentheses, and other syntactical tricks, you can usually (if not always) replace one with the other; however, this is typically not a good idea. [For more on this, see "Re: Code blocks with ternary operator or trailing conditionals", which I wrote a few weeks ago.]

    The ternary operator can be used in any list, not just to generate an array, so an improvement on the print examples you show, might be (using equivalent code):

    print 1<2 ? '1' : '2', $/;

    Although, perhaps a more likely scenario might be something like:

    $ perl -Mstrict -Mwarnings -e ' for my $item_count (0..2) { print "$item_count item", $item_count == 1 ? "" : "s", ".\n" } ' 0 items. 1 item. 2 items.

    When using the ternary operator, take care with precedence issues and add parentheses where needed (the documentation shows some examples).

    A final point, unrelated to conditionals, regarding your test:

    my $arrref = \( if(1<2){'1'}else{()} );

    Taking a reference to a list actually returns a list of references to each item in the original list. Accordingly, it would probably be a good idea to avoid any instances of:

    $arrayref = \(...);

    While that may work when first written using a list with only one item, any subsequent modifications which result in additional list items will cause problems. A better option would be:

    $arrayref = [...];

    Item 2. of "perlref: Making References" has more information on this.

    -- Ken

      Taking a reference to a list actually returns a list of references to each item in the original list. ... probably ... good ... to avoid ...:

      $arrayref = \(...);

      E.g.:

      c:\@Work\Perl\monks>perl -wMstrict -le "my $not_really_an_arrayref = \('one', 'two', 'three'); print $$not_really_an_arrayref; " three

        Good point; however, the way you'd normally dereference an arrayref generates a fatal runtime error:

        $ perl -le ' my $think_this_is_an_arrayref = \(qw{one two three}); print qq{Probably not dereference like this: $$think_this_is_an_ar +rayref}; print qq{Probably would dereference like this: @$think_this_is_an_ +arrayref}; ' Probably not dereference like this: three Not an ARRAY reference at -e line 4.

        -- Ken

Re: conditional statements in list constructors
by Anonymous Monk on Mar 27, 2014 at 14:08 UTC
Re: conditional statements in list constructors
by Don Coyote (Hermit) on Mar 28, 2014 at 01:10 UTC

    Thanks for clarifying my use of conditional. The question has been answered expertly. I did mean to write compound rather than conditional though. my bad:\

    great insights, I have not seen the x!!() operator before.

    My question, wrongly put, is really in the second paragraph, why does the if compound statement not compile in list constructors?

    using MO=Deparse showed me the if compound statement reduces to a do block so I can take some comfort in my not quite correctly placed assumptions. That is, I was trying to use the if statement as a conditional or rvalue, within a list constructor, when it simply is not one.

    The reason for me showing the examples, was to show that context did not make a difference in the syntax compilation, in case that did have a relevance, unfortunately my miss-worded question let me down here.

    I was just confused by an if statement not compiling within a list context.

    So while thanking you for reminding me about the particular instance of taking a ref to a list, which you will be glad to know, has now sunk in, the if statement within the list does not actually compile. Should it have done in the way the ternary conditional does, then I am sure there are cases where taking refs to the last item in the resultant list do come in useful.

    And look, if the vogons recite the virtues of tertiary operators in the poetic redrafts of high order mouse manuals volumes XXXII thru VL then who or what am I to argue?...ahem,oops

    my $reftolastscalar = \( "if(1<2){'1'}else{()}" ); my @arr = ( "if(1<2){'1'}else{()}" ); # ref to '2' ? # my $reftolastscalarfromlist = # \('0.99', if(1>2){1}else{(1,2)} ); # does not compile # ref to '42' ? my $reftolastscalarfromlist = \('0.99', 1>2?1:(1,42)); {local $, = "\n"; print $$reftolastscalar,@arr,$$reftolastscalarfromlist,''; # Deparses to: do { print '1','2' }; # :) if(1>2){print '1'}else{print ('1','2')} ; print '',''; } exit 0; ########OP########## if(1<2){'1'}else{()} if(1<2){'1'}else{()} 42 1 2 user@Desktop:~/Desktop$