Why `for/if` syntax is not allowed in loops

Hi,

I wonder why the combination of for and if is allowed in comprehension like

 nbe = sum(count(iseven,rv) for rv ∈ rvv if isodd(length(rv));init=0)

but has to be written in two separate control flows statement in loops:

  #separate for and if in the loop form
    nbe = 0
    for rv ∈ rvv 
        if isodd(length(rv))
            nbe += count(iseven,rv)
        end
    end

and can’t be written as

    nbe = 0
    for rv ∈ rvv if isodd(length(rv))
        nbe += count(iseven,rv)
    end
MWE
function test_for_if()

    rvv = [[rand(Int)  for s ∈ 1:rand(1:10)] for sn ∈ 1:10]
    @show rvv

    #separate for and if in the loop form
    nbe = 0
    for rv ∈ rvv 
        if isodd(length(rv))
            nbe += count(iseven,rv)
        end
    end
    
    #the comprehension for allows for for-if syntax
    nbe′ = sum(count(iseven,rv) for rv ∈ rvv if isodd(length(rv));init=0)

    @show nbe,nbe′

    #why the following for-if syntax doesnt' apply for loops ?
    # nbe′′ = 0
    # for rv ∈ rvv if isodd(length(rv))
    #     nbe′′ += count(iseven,rv)
    # end
    # @show nbe,nbe′,nbe′′
    return nothing
end
1 Like

I think the main reason is that it introduces a parsing ambiguity that depends on whitespace. Your first example needs two end. Your second needs one.

The sum doesn’t have this ambiguity because generators to not use end.

You can achieve you intend with the somewhat horrific:

julia> for a in (a for a in 1:3 if isodd(a))
           @show a
       end
a = 1
a = 3
8 Likes

@LaurentPlagne, I am unsure if your question is why the language designers didn’t accommodate the syntax you are inquiring about or why you experience the issue when using the not supported for/if combination.

In scenarios like yours - and when comprehension is not the best pick, I like to do something like the following:

for x in 1:3
    !isodd(x) && continue
    @show x
end
# x = 1
# x = 3
6 Likes

I think @odow already sufficiently answered the question of “why”. But if you’re also looking for working alternatives, you could use Iterators.filter:

using .Iterators

nbe3 = 0
for rv in Iterators.filter(isodd ∘ length, rvv)
     nbe3 += count(iseven, rv)
end

In other situations you would have to rely on anonymous functions (e.g. for rv in Iterators.filter(rv -> isodd(length(rv), rvv)) though, which to me seems more complicated than a simple if ... end or &&/|| continue.

3 Likes

thank you all for these detailed answers !

3 Likes

Linking identical thread for further insights.

3 Likes

I’ll just point out that

can be written as

    for rv ∈ rvv if isodd(length(rv))
        nbe += count(iseven,rv)
    end end

:wink:

1 Like