How do I put a conditional and a loop in the same line?

Viewed 187

I would like to put a conditional and loop on the same line on Perl. This is what I would like to achieve:

foreach (0 .. 10) {
    print $_ if $_ % 2;
}

which should give me 13579. I'm looking for a statement like loop comprehension in Python, e.g.

[i for i in range(10) if i % 2]

I know that print $_ foreach 0 .. 10; works but the problem is adding the conditional to the statement as well...

5 Answers

For elements of a list which are odd

$_%2 and print for 0..10;

or

$_&1 and print for 0..10;

Or one may want elements at odd indices where that 0..10 stands for array indices

my @ary = 0..10;

$_&1 and print $ary[$_] for 0..$#ary;

The syntax $#ary is for the index of the last element of array @ary.

The point being that Perl's and short-circuits so if the expression on its left-hand-side isn't true then what is on its right-hand-side doesn't run. (Same with or -- if the LHS is true the RHS isn't evaluated.)

If you indeed need array elements at odd indices can also do

my @ary = 0..10; 

print for @ary[grep { $_&1 } 0..$#ary];

All above print 13579 (no linefeed). In a one-liner, to enter and run on the command-line

perl -we'$_%2 and print for 0..10'

If you actually need to print each number on its own line, judged by a posted comment, replace print with say, like

$_%2 and say for 0..$#ary;

for odd array elements from array @ary, or

$_&1 and say $ary[$_] for 0..$#ary;

for array elements at odd indices.

Add use feature 'say'; to the beginning of the program (unless it is enabled already by other loaded tools/libraries).

Why not something like below?

use warnings;
use strict;

print ($_ % 2 ? $_ : "") for (1..10);

If you want to build a specific list, you can always use a classic c-style for loop. Also, if you want it "on one line", you can just cram it onto one line. Perl isn't Python, there are no requirements on line breaks or indentation. However, putting code on a single line reduces readability.

for (my $i = 1; $i <= 10; $i += 2) { print $i; }

You don't have to build the list and remove half of it this way. There might be some performance difference, and you can always Benchmark it, but it smells like premature optimization to me.

For that matter, you can use a while loop too if you like, using the same logic.

my $i = 1; while ($i <= 10) { print $i; $i += 2; }

Perl isn't Python. List comprehensions are nice, but Perl doesn't have them.

The closest perl idiom is probably something like

grep $_ % 2 , (0..9);

A 1-liner to try it out:

$ perl -e '@a = grep $_ % 2 , (0..9); print "@a\n"'
1 3 5 7 9

Note there's probably a performance difference in both memory and time. I'm pretty sure Perl will build the complete list of 10 items and filter it to make a new list. The list comprehension can build the filtered list directly.

The general answer is to use grep.

say for grep $_ % 2, 0..10;

As a case-specific solution, you can use the following:

say $_ * 2 for 0..5;
Related