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

Well, I did a search, and Nested Ternary Operators and Formatting chained and nested ternary ops (was Re^4: lvalue subs return undef....) didn't really help. Can anyone see why it's not giving 33 or 18 for $result?
#!/usr/bin/perl -w use strict; my $major_length = 8552; my $val = 4; # this gives $result as 33, as expected .... my $result = ($major_length == 4800) ? 18 : ($major_length == 8552) ? 33 : 0; # this gives $result as 1, not 33. Makes sense, # since $val == 4 evaluates to 1. But, I want $result to # be assigned 33, based on the fact that $val == 4. $result = ($major_length == 4800) ? 18 : ($major_length == 8552) ? ($val == 4) : 33; # this is what I had originally; it gives $result as 1, # not 33. $result = ($major_length == 4800) ? 18 : ($major_length == 8552) ? ($val == 4) : 33 ? ($val == 1) : 18; # Putting in parens doesn't help either # this gives $result as 1 - but # I want it to equal 18 or 33... $result = ($major_length == 4800) ? 18 : ( ($major_length == 8552) ? ($val == 4) : ( 33 ? ($val == 1) : 18 ) ); print "$result\n";

-- Burvil

Replies are listed 'Best First'.
Re: nested tabular ternary
by diotalevi (Canon) on Apr 11, 2006 at 01:15 UTC

    You weren't nesting your ternaries. You seemed to think you were but you weren't. You were putting the next ternary's conditional into the result field of the previous ternary. You should have put the entire next ternary - conditional, result a, and result b - into the result field of the enclosing ternary.

    $result = ($major_length == 4800) ? 18 : ($major_length == 8552 and $val == 4) ? 33 : die "not handled";

    or...

    $result = ($major_length == 4800) ? 18 : ($major_length == 8552) ? ($val == 4 ? 33 : $val == 1 ? 18 : die +"not handled" ) : die "not handled";

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      Update: - fixed screwed-up formatting within code tags.

      Just an observation regards style. (And please don't get me wrong - this is just an observation, not a critisism - each to their own :)

      I find it interesting that both yours and Liverpole's answer use a similar layout style for the ternary. That is - to break the line after an operator.

      This of course goes against what is recommended by TheDamian in PBP (pp. 27-29 & 121-123). Personally, I prefer his recommended style, and I would have written your second example like so:

      $result = ($major_length == 4800) ? 18 : ($major_length == 8552) ? ($val == 4 ? 33 : $val == 1 ? 18 : die "not handled") : die "not handled";
      To me, this is more readable - it looks more like a "table", and it's more obvious that there is some nesting. Also, having an operator at the start of a line makes it much more obvious that it's a continuation of the previous line.

      Cheers,
      Darren :)

        I don't see the "table" and it looks like you just scattered your code all across the page, as if it were scrambled.

        No wait.. it looks like a bad translation of the original lisp into formatted perl. It really does look like lisp indenting conventions were attempting to be followed in your example.

        (if (= major-length 4800) 18 (if (= major-length 8552) (if (= val 4) 33 (if (= val 1) 18 (error "not handled"))) (error "not handled")))

        Here's a nicer lisp version of the same thing. It's got more in common with what I originally suggested than does what you alleged TheDamian recommended.

        (cond ((= major-length 4800) 18) ((= major-length 8552) (cond ((= val 4) 33) ((= val 1) 18) (error "not handled"))) (error "not handled"))

        The above is just a more verbose version of the following. This is getting to be seriously terse to the point that there's almost not enough whitespace in there. I'm just following standard indenting rules so this is just stock lisp.

        (ecase major-length (4800 18) (8552 (ecase var (4 33) (1 18))))

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

        I can't make heads or tail of your "more readable" "table". Did you mistakenly used tabs?

        Aligning ? and : provides the best results for me. Parens provide a visual scope:

        $result = ($major_length == 4800 ? 18 : ($major_length == 8552 ? ($val == 4 ? 33 : ($val == 1 ? 18 : die "not handled") ) : die "not handled" ) );

        And then there's Perlish:

        ; $result = ($major_length == 4800 ? 18 : ($major_length == 8552 ? ($val == + 4 ? 33 + : ($val == 1 ? 18 + : die "not handled") ) : die "not + handled" ) )

        None of these are particularly readable &mdash or maintainable. if should be used here.

Re: nested tabular ternary
by liverpole (Monsignor) on Apr 11, 2006 at 01:17 UTC
    Looking at just this part of your code:
    # this gives $result as 1, not 33. Makes sense, # since $val == 4 evaluates to 1. But, I want $result to # be assigned 33, based on the fact that $val == 4. $result = ($major_length == 4800) ? 18 : ($major_length == 8552) ? ($val == 4) : 33;
    If I understand what you're asking for, you want:
    if ($major_length == 4800) { $result = 18; } elsif ($major_length == 8552) { if ($val == 4) { $result = 33; } else { # $result is Unspecified (see A below) } } else { # $result is Unspecified (see B below) }
    You can do this with nested ternaries (though it's a little messy), but if you only want to use ternaries, you'll have to specify what to set $result to if none of the conditions is met (eg. $result = undef):
    $result = ($major_length == 4800)? 18: (($major_length == 8552)? (($val == 4)? 33: undef): # see A above undef); # see B above
    But it's messy as I said, and unless you really want to set $result to some value, it's probably better to use conditionals (eg. if...else), both for your clarity and the clarity of those who may read your code in the future ;-)

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: nested tabular ternary
by Errto (Vicar) on Apr 11, 2006 at 01:26 UTC
    I think the problem here is you haven't really laid out all the possible scenarios. Let's try doing this in English and we'll see where we get:
    • If $major_length is 4800, return 18
    • Otherwise, If $major_length is 8552:
      • If $val is 4, return 33
      • If $val is 1, return 18
      • Otherwise, ?????
    • Otherwise, ?????
    You haven't addressed the possibility that (major_length is neither 4800 nor 8852) nor that (major_length is 8852 but val is neither 1 nor 4). Now, it may be that in your application these scenarios are impossible, but the ?: operator does not allow you to simple ignore them. You may find it easier to use an if-else construct instead:
    if ($major_length == 4800) { $result = 18; } elsif ($major_length == 8552) { if ($val == 4) { $result = 33; } elsif ($val == 1) { $result = 18; } }
    You still have the possibility of those unhandled situations, but now they'll simply result in $result not getting assigned at all.
Re: nested tabular ternary
by GrandFather (Saint) on Apr 11, 2006 at 01:40 UTC

    If the real code is in the context of a sub then the following is clearer and less error prone:

    return 18 if $major_length == 4800; if ($major_length == 8552) { return 33 if $val == 4; return 18 if $val == 1; return "Bogus value ($val) for major length $major_length"; } else { return "Bogus major length ($major_length)"; }

    DWIM is Perl's answer to Gödel
Re: nested tabular ternary
by nedals (Deacon) on Apr 11, 2006 at 06:36 UTC
    As stated...

    diotalevi: You weren't nesting your ternaries.

    A little trick I like to use is to avoid putting a nested trenary the 'then' part.
    I had a little difficulty deciphering your desired result, but here's the general idea.

    $result = ($major_length == 4800) ? 18 : ($major_length != 8552) ? 0 : ($val == 4) ? 33 : ($val == 1) ? 18 : 0;