Julia throws undefined error when the variable is defined - julia

I have this simple while loop that uses i = 1 as an index.
global i = 1
n = rows
while i <= n
if prod(isa.(collect((y)[i,:]),Number))==0
delete!(y,i)
x_axis = x_axis[1:end .!= i]
n -= 1
end
i += 1
end
but I'm getting this error:
UndefVarError: i not defined
top-level scope#Local: 23
I even made my i global as per the suggestion on some similar questions on SO but the error persists. I am running this on Pluto.jl so maybe it could be an environment issue.

Firstly, note that if you use Julia v1.5+ then you don't need to make i a global (example below with current stable version v1.5.4):
julia> i = 1
1
julia> while i < 5
println(i)
i += 1 # <----- this works in Julia v1.5+
end
1
2
3
4
However, it seems you are using Julia v1.4 of older, in which case I think Logan Kilpatrick gave you the answer: You need to make i a global from inside the while loop's scope. As Logan mentioned, try adding global where you increment i, like in this example from the while function's docs:
julia> i = 1 ;
julia> while i < 5
println(i)
global i += 1 # <------------ Try this!
end
1
2
3
4
Note also that you don't need to specify it's a global if your while loop is inside a function, as in
julia> function foo(istart)
i = istart
while i < 5
println(i)
i += 1 # <-- 'global' not needed inside a function!
end
end
foo (generic function with 1 method)
julia> foo(1)
1
2
3
4

You have hitted the "ambiguous soft scope case".
In short: the assignment of a local variable inside a (soft) local scope depends if the code is inside a "REPL context"
or not.
For "REPL context" I mean the REPL and all environments that behaves as the REPL in this case, for example
Jupyter:
julia> i = 0
julia> while i < 3
i += 1
#info i
end
[ Info: 1
[ Info: 2
[ Info: 3
Instead code from non interactive context like file, eval and Pluto acts like that:
julia> code = """
i = 0
while i < 3
i += 1
#info i
end
"""
julia> include_string(Main, code)
┌ Warning: Assignment to `i` in soft scope is ambiguous because a global variable by the same name exists: `i` will be treated as a new local. Disambiguate by using `local i` to suppress this warning or `global i` to assign to the existing global variable.
└ # string:3
ERROR: LoadError: UndefVarError: i not defined
All of this has been designed to ensure both the convenience of REPL usage and to avoid unwanted side effects of using julia on a large scale.
Full details here.
To fix the problem you may be use global as already suggested or enclose your code inside a function.

Pluto implicitly wraps a cell into a function, see https://github.com/fonsp/Pluto.jl/pull/720, therefore the global annotation or explicit wrapping into a function should not be required.
Putting the following into a Pluto cells works for me:
begin
i = 1
n = 100
while i<=n
if i % 2 == 0
n -= 1
end
i += 1
end
end
The implicit function wrapping is disabled when macros are used inside a cell (which prevents Pluto to gather reactivity information), therefore the following does not work in Pluto due to the Julia scoping rules:
begin
i = 1
n = 100
while i<=n
if i % 2 == 0
n -= 1
end
#show i += 1
end
end
Throws:
UndefVarError: i not defined
top-level scope#Local: 5[inlined]
top-level scope#none:0

Related

Julia Distributed - why the variable is not defined on worker after evaluating the expression?

function pass_object(src::Int, target::Vector{Int8}, name::Symbol; from_mod=Main, to_mod=Main)
r = RemoteChannel(src)
#sync #spawnat(src, put!(r, getfield(from_mod, name)))
#sync for to in target
#spawnat(to, Core.eval(to_mod, Expr(:(=), name, fetch(r))))
end
end
variable1 = some_data
#sync pass_object(1, pid, :variable1)
result = #spawnat pid Core.eval(Main, :(new_variable=variable1))
Julia 1.5.3
I tried multiple ways how to pass object to another worker and then use the assigned variable, but the result is always that it is not defined. There are no errors before I try to access its attribute, e.g. remotecall_fetch(println, pid, new_variable.label)
Any ideas why this does not work? Thank you for any help.
Use ParallelDataTransfer for that. For an example.
using ParallelDataTransfer, Distributed
addprocs(1)
#everywhere using ParallelDataTransfer, Distributed
variable1 = [1,2,3]
#passobj 1 2 variable1
Now some console tests:
julia> fetch(#spawnat 2 sum(variable1))
6
julia> fetch(#spawnat 2 variable1[1]=1000)
1000
julia> sum(variable1)
6
julia> fetch(#spawnat 2 sum(variable1))
1005

Trying to understand Julia macro #isdefined

Hi I'm trying to understand how the macro #isdefined works.
I was expecting Chunk 1 to print out 1 2 3 4, but it is not printing anything.
Also related, I was expecting chunk 2 to print out 2 3 4 5, but it is throwing an error: "a is not defined".
# Chunk 1
for i = 1:5
if #isdefined a
print(a)
end
a = i
end
# Chunk 2
for i = 1:5
if i > 1
print(a)
end
a = i
end
Could someone help explain what is wrong about each chunk? Thank you.
The reason is that a is a local variable in the scope of for loop. Now the crucial part is that for loop follows the following rule defined here:
for loops, while loops, and comprehensions have the following behavior: any new variables introduced in their body scopes are freshly allocated for each loop iteration
This means that assignment to a at the end of the loop does not carry over to the next iteration, because when the new iteration starts the old value of a is discarded as a is freshly allocated. It only gets defined after a=i assignment.
Therefore you have the following behavior:
julia> for i = 1:5
if #isdefined a
println("before: ", a)
end
a = i
if #isdefined a
println("after: ", a)
end
end
after: 1
after: 2
after: 3
after: 4
after: 5
However, if a is defined in an outer scope, then its value is not for loop local and is preserved between iterations, so you have for instance:
julia> let a
for i = 1:5
if #isdefined a
println("before: ", a)
end
a = i
if #isdefined a
println("after: ", a)
end
end
end
after: 1
before: 1
after: 2
before: 2
after: 3
before: 3
after: 4
before: 4
after: 5
and
julia> let a
for i = 1:5
if i > 1
println(a)
end
a = i
end
end
1
2
3
4
I have used let block but it could be any kind of outer scope except global scope (in which case you would have to change a = i to global a = i to get the same effect).

How variable behave in for loop have changed

Consider this source code
println("Julia language version ",VERSION)
i=666
for i = 1:2
println("i is $i")
end
println("global i is $i")
function main()
j = 666
for j = 1:2
println("j is $j")
end
println("global j is $j")
end
main()
Consider the output of version 0.6
Julia language version 0.6.3
i is 1
i is 2
global i is 2
j is 1
j is 2
global j is 2
Compare to the output of version 1.0
Julia language version 1.0.0
i is 1
i is 2
global i is 666
j is 1
j is 2
global j is 666
I can't change the value of variable i and variable j using a for loop like I can before in version 0.6
I think C programmers will have the shock of their lives...
If you use Julia 0.7 (which is basically == 1.0 with deprecations) you would see the necessary deprecation messages for the intended behaviour change:
┌ Warning: Deprecated syntax `implicit assignment to global variable `i``.
│ Use `global i` instead.
└ # none:0
┌ Warning: Loop variable `i` overwrites a variable in an enclosing scope. In the future the variable will be local to the loop instead.
└ # none:0
i is 1
i is 2
global i is 2
So to get what you want you could write:
function main()
global j = 666
for j = 1:2
println("j is $j")
end
println("global j is $j")
end
main()
Your first example on the global level should theoretically be dealt with using for outer i.. as described in https://docs.julialang.org/en/latest/manual/variables-and-scoping/#For-Loops-and-Comprehensions-1 but currently this is not handled in the REPL. See this issue: https://github.com/JuliaLang/julia/issues/24293

Can I use <- instead of = in Julia?

Like in R:
a <- 2
or even better
a ← 2
which should translate to
a = 2
and if possible respect method overloading.
= is overloaded (not in the multiple dispatch sense) a lot in Julia.
It binds a new variable. As in a = 3. You won't be able to use ← instead of = in this context, because you can't overload binding in Julia.
It gets lowered to setindex!. As in, a[i] = b gets lowered to setindex!(a, b, i). Unfortunately, setindex! takes 3 variables while ← can only take 2 variables. So you can't overload = with 3 variables.
But, you can use only 2 variables and overload a[:] = b, for example. So, you can define ←(a,b) = (a[:] = b) or ←(a,b) = setindex!(a,b,:).
a .= b gets lowered to (Base.broadcast!)(Base.identity, a, b). You can overload this by defining ←(a,b) = (a .= b) or ←(a,b) = (Base.broadcast!)(Base.identity, a, b).
So, there are two potentially nice ways of using ←. Good luck ;)
Btw, if you really want to use ← to do binding (like in 1.), the only way to do it is using macros. But then, you will have to write a macro in front of every single assignment, which doesn't look very good.
Also, if you want to explore how operators get lowered in Julia, do f(a,b) = (a .= b), for example, and then #code_lowered f(x,y).
No. = is not an operator in Julia, and cannot be assigned to another symbol.
Disclaimer: You are fully responsible if you will try my (still beginner's) experiments bellow! :P
MacroHelper is module ( big thanks to #Alexander_Morley and #DanGetz for help ) I plan to play with in future and we could probably try it here :
julia> module MacroHelper
# modified from the julia source ./test/parse.jl
function parseall(str)
pos = start(str)
exs = []
while !done(str, pos)
ex, pos = parse(str, pos) # returns next starting point as well as expr
ex.head == :toplevel ? append!(exs, ex.args) : push!(exs, ex)
end
if length(exs) == 0
throw(ParseError("end of input"))
elseif length(exs) == 1
return exs[1]
else
return Expr(:block, exs...) # convert the array of expressions
# back to a single expression
end
end
end;
With module above you could define simple test "language":
julia> module TstLang
export #tst_cmd
import MacroHelper
macro tst_cmd(a)
b = replace("$a", "←", "=") # just simply replacing ←
# in real life you would probably like
# to escape comments, strings etc
return MacroHelper.parseall(b)
end
end;
And by using it you could probably get what you want:
julia> using TstLang
julia> tst```
a ← 3
println(a)
a +← a + 3 # maybe not wanted? :P
```
3
9
What about performance?
julia> function test1()
a = 3
a += a + 3
end;
julia> function test2()
tst```
a ← 3
a +← a + 3
```
end;
julia> test1(); #time test1();
0.000002 seconds (4 allocations: 160 bytes)
julia> test2(); #time test2();
0.000002 seconds (4 allocations: 160 bytes)
If you like to see syntax highlight (for example in atom editor) then you need to use it differently:
function test3()
#tst_cmd begin
a ← 3
a ← a + a + 3 # parser don't allow you to use "+←" here!
end
end;
We could hope that future Julia IDEs could syntax highlight cmd macros too. :)
What could be problem with "solution" above? I am not so experienced julian so many things. :P (in this moment something about "macro hygiene" and "global scope" comes to mind...)
But what you want is IMHO good for some domain specific languages and not to redefine basic of language! It is because readability very counts and if everybody will redefine everything then it will end in Tower of Babel...

Julia codegeneration with global function

julia 0.5.1
I want to create a function inside a quote that can be used after the specified macro has been used. Here is an example of what I mean
macro wat()
quote
type Foo end
global bar() = begin end
global function bar2()
end
type Baaz end
end
end
#wat
Foo()
Baaz()
bar()
bar2()
Now when I run this the last line crashes, because bar2 is undefined. I do not understand why because in my understanding bar() and bar2() should be equal and bar is just syntactic sugar for bar2. But they are apparently not equal and I do not understand why the one works and other does not.
Secondly is there a way to define bar and bar2 inside that quote without the global-keyword and still being available after the macro has been executed?
My motivation for wanting the bar2 notation is that I can specify a return-type with this syntax.
global bar3()::Void = begin end
Is not allowed syntax.
In the returned expressions of Julia macros, names of local variables are replaced with unique symbols:
julia> macro foo()
quote
x = 1
global y = 2
end
end
#foo (macro with 1 method)
julia> macroexpand(:(#foo))
quote # REPL[1], line 4:
#1#x = 1
global y = 2
end
This feature is called macro hygiene and avoids accidental clashes with variables at the call site.
To escape from this behavior, one has to use esc:
julia> macro bar()
quote
x = 1
end |> esc
end
#bar (macro with 1 method)
julia> macroexpand(:(#bar))
quote # REPL[1], line 3:
x = 1
end
Often, one doesn't want to escape the whole returned expression but only specific parts of it:
julia> macro myshow(expr)
quote
x = $(esc(expr))
println($(string(expr)), " = ", x)
x
end
end
#myshow (macro with 1 method)
julia> x = pi/2
1.5707963267948966
julia> macroexpand(:(#myshow sin(x)))
quote # REPL[1], line 3:
#1#x = sin(x) # REPL[1], line 4:
(Main.println)("sin(x)", " = ", #1#x) # REPL[1], line 5:
#1#x
end
julia> #myshow sin(x)
sin(x) = 1.0
1.0
julia> x
1.5707963267948966
For details, I recommend to read the corresponding section in the manual.

Resources