Is there a way to have a non-local scope loop index, so code similar to this:
for i = 1:9
if i > 3
break
end
end
println(i)
works and I don’t have to write:
j = 0
for i = 1:9
if i > 3
j = i
break
end
end
println(j)
Is there a way to have a non-local scope loop index, so code similar to this:
for i = 1:9
if i > 3
break
end
end
println(i)
works and I don’t have to write:
j = 0
for i = 1:9
if i > 3
j = i
break
end
end
println(j)
no easy / canonical way because for...end
forms a local scope, there’s this hack:
julia> j = Ref(-1)
Base.RefValue{Int64}(-1)
julia> for j[] = 1:10
if j[]>3
break
end
end
julia> j[]
4
But I’d recommend against it.
Was for global i = 1:9
syntax ever proposed?
Are you looking for the outer
keyword?
function foo(p)
local i
for outer i = 1:100
rand() > p && break
end
return i
end
, first time seeing this anywhere and ?outer
has nothing to offer, neither do for
’s manual page or docs. Btw you don’t need the first local
I think.
Same here. Really hard to find.
It does seem hard to find. The documentation mentions this feature here:
https://docs.julialang.org/en/v1/manual/variables-and-scoping/#Loops-and-Comprehensions
True. This works fine:
function foo(p)
i = 0
for outer i = 1:100
rand() > p && break
end
return i
end
You just need the variable to exist in the outer scope. Using local
is the least constraining way of doing that.
For example if you decide later to change the type of the loop variable (e.g. to for i = 1.0 : 10.0
), having i=0
hardcoded will lead to a type instability. With local, i
will always have the right type.
Another advantage is that you can reliably check whether the loop ran at all:
julia> function testfun(p)
local i
for i = 1:100
rand() > p && break
end
if @isdefined(i)
return i
else
return 999
end
end
testfun (generic function with 1 method)
julia> testfun(0.99)
999
In this case it would be easier to just return I from the loop?
You mean return i
, right? My actual use case is more complex and I can’t do it, but in the foo
example above, it is definitely an option.
In this case I would rather write:
function testfun(p)
i = 999
for outer i = 1:100
rand() > p && break
end
return i
end
but I see where local
may be useful.
BTW, outer
is not recognized as a keyword by Discourse.
That’s because it’s not actually a reserved keyword, outer
only has special meaning in the iteration specification of for loops and generators. That’s why the following are still completely valid syntax:
julia> outer = 7
7
julia> for outer in 1:3
println(outer)
end
1
2
3
julia> for outer * inner ∈ 1:3
println(1 * 2)
end
1
2
3
Hmm, that looks quite a bit error-prone.
Why is that error prone? It would be pretty annoying not being able to name variables outer
in normal code anymore, just for this special for loop syntax.
(I had no idea what for outer * inner ∈ 1:3
does and I had to stare at Meta.@lower
output for a while. This is so diabolical and I love this example )
OK, maybe not error-prone, since compiler will help here, but context sensitive behavior of outer
may be a bit confusing. I don’t think identifiers, which may or may not be restricted keywords depending on context, are widely used in programming languages. Maybe I’m too old date…
Way too diabolical for me! Can you explain what is happening there? Why are we getting anything other than 2
as in:
julia> println(1 * 2)
2
I don’t want spoil anyone’s fun so I’m hiding the explanation behind [spoiler]
(click to remove the blur):
for outer * inner ∈ 1:3
println(1 * 2)
end
behaves as above since it is equivalent to
for x ∈ 1:3
_ * _ = x
println(:this_is_ignored * :this_is_also_ignored)
end
(Note also that _ * _ = x
is equivalent to *(_, _) = x
; i.e., it defines a closure named *
.)
Perhaps the best way to see what is going on is to print what *
is:
julia> for outer * inner ∈ 1:3
@show *
println(:ignored * :ignored)
end
* = var"#*#12"{Int64}(1)
1
* = var"#*#12"{Int64}(2)
2
* = var"#*#12"{Int64}(3)
3