Jon Bot
Jon Bot

Reputation: 87

Ternary operator doesn't allow iterative operator in it, but if-else does?

I noticed that if I replace the if-else statement I'm using with a ternary operator I end getting a compilation error when I try and run my code. I believe the culprit is the foreach() loop I have inside my if-else. Do you know why the ternary operator isn't behaving the same as the if-else construct in this instance?

My code looks like this

#!/program/perl_v5.6.1/bin/perl5.6.1

use strict;
use warnings;


my $fruits_array_ref = &get_fruits();
if($fruits_array_ref != 0) {
    print("$_ is a fruit.\n") foreach(@$fruits_array_ref);
}
else {
    print("Maybe you like vegetables?\n");
}


sub get_fruits() {
    my @fruit_list;
    my $shopping_list = "/home/lr625/grocery_items";

    open(my $shopping_list_h, "<", $shopping_list) or die("Couldn't open.\n");
    while(my $line = <$shopping_list_h>) {
        next if $line =~ /^\#/;
        chomp($line);
        push(@fruit_list, $line);
    }
    close($shopping_list_h) or die("Couldn't close.\n");

    scalar(@fruit_list) > 0 ? return(\@fruit_list) : return(0);
}

My data in the grocery list looks like

# this is a header
# to my grocery list
apple
banana
grape
orange

I'm replacing the if-else with a ?: operator to look like this now in the main function.

my $fruits_array_ref = &get_fruits();
$fruits_array_ref != 0 ? print("$_ is a fruit.\n") foreach(@$fruits_array_ref) : print("Maybe you like vegetables?\n");

Also, for reference my error says.

syntax error at test.pl line 8, near ") foreach"

Execution of test.pl aborted due to compilation errors.

Upvotes: 3

Views: 246

Answers (4)

zdim
zdim

Reputation: 66883

The ternary operator takes arguments before ? and :, see in perlop. It can evaluate an expression and use its result for this. But a loop is not an expression and cannot 'run' inside.

For a demonstration -- you could, if you insisted, call a function which will as a side effect print

sub greet { say "hello" for 1..3 }

my $x = 1;
($x == 1) ? greet() : say "bye";

Actualy doing this in production code is a different matter and would likely be a bad idea. The whole point would be to rely entirely on side effects, opposite to what we normally want to do.


To explain my comment above -- the main point of the ternary operator is to return a value, with a choice between two values, in one statement. While it is "equivalent" to an if-else, its use is (ideally) meant to be very different. So doing some other processing inside the ?: arguments, in any way, is really an abuse of notation, a side-effect, since they are intended to produce a value to be returned. Printing out of it is opposite to the idea of producing and returning a value. This is not a criticism, the operator is used often and by many as a syntactic shortcut.

In this sense I would recommend to revert to an if-else for doing what is shown.

Upvotes: 1

ysth
ysth

Reputation: 98388

The foreach statement modifier can only be used at the end of a statement.

Why are you using ?:? You would normally only do that if you wanted a single result.

You could wrap the print...foreach... in a do {...}, or you could use map instead of foreach. Or just leave it as an if/else.

Upvotes: 1

reflective_mind
reflective_mind

Reputation: 1515

The other answers already pointed out that you can't use the ternary operator the way you tried. For the sake of completeness and to give you some sensible use cases, take a look at the following examples:

#1: Used as a subroutine argument

testSub($var eq 'test' ? 'foo' : 'bar');

Here you can see how the subroutine testSub is called with the argument foo if $var equals the string test. Otherwise testSub will be called with bar. This is useful because you cannot use an if-else structure as a sub argument.

#2: Used for conditional assignment

my $result = $var eq 'test' ? 'foo' : 'bar'; # $result will contain 'foo' or 'bar'

The ternary operator is not meant as a simple replacement to an if-else structure. Since it returns a value (here either foo or bar) it makes sense to also use this value. If you don't intend to use the returned value, you should go for the usual if-else instead.

Upvotes: 4

choroba
choroba

Reputation: 241818

if-else is a flow control structure, ?-: is an operator that takes expressions as operands. foreach is a flow control structure, not an expression.

You can turn any block of code into an expression by using do:

$fruits_array_ref != 0
    ? do { print "$_ is a fruit.\n" for @$fruits_array_ref }
    : print "Maybe you like vegetables?\n";

But why?

Upvotes: 9

Related Questions