I have a problem due to scoping rules of while loop and try-catch block. I have read similar topics on the forum, but the answers seems to be inapplicable in my case.
My problem is the following. I have a function that can fail due to numerical issues. When the function fails, I want to retry the calculation. This function returns a vector which dimension is not known a priori.
Here is my attempt:
function f(x, y)
# x and y are vectors
success = false
stop = 0
local z # z is a vector whose dimension is not known
while !success && (stop < 10)
try
z = compute(x, y)
success = true
catch e
@warn "Retry..."
stop += 1
end
end
if stop == 10
error("Fail!")
end
return z
end
Such a code structure return the following error:
ERROR: UndefVarError: `z` not defined in local scope
Suggestion: check for an assignment to a local variable that shadows a global of the same name.
I understand why the problem arises, but I don’t figure out how to solve it.
I don’t really understand the issue: your code already works as intended as far as I can tell (if you add the while’s end, and maybe also give the function a name, say f)? E.g. for compute(x, y) = x + y, f(x, y) returns x + y without issues, while for compute(x, y) = error(), f(x, y) throws the "Fail!"error, never reaching the return z.
By the way, note that if your function is deterministic, running it again won’t help with numerical issues. Of course, if you’re using RNG, or the order of operations might change due to thread scheduling, it might help.
Start with the above commenter’s remarks. But if that doesn’t resolve all your issues, note that try introduces a new scope. I don’t have a REPL handy to test this (or whether it’s even necessary, since the above poster says what you had was almost fine), but if you run into problems you can try to instead retrieve values from the scope like so:
z, success = try
c = compute(x, y) # you could name this `z`, but a different name is probably clearer
c, true # will be assigned to the outer `z, success` when the result of this line is "returned" from this block
catch e
# ...
z, false # this probably works, but I haven't tested it. Sorry.
end
It’s not clear how you’re running into that error. Could you provide a fully working example? That means that if anybody copies the code and runs it themselves, they’d see the same error you did. At minimum you need to provide compute and the call of that nameless function. If you can simplify compute to less code and dependencies without removing the error, you should.
That shouldn’t matter, the local z line declares a local variable in that local scope level, and nested local scopes will reassign that same variable by default. That’s why eldee’s non-erroring compute worked.
This code works perfectly as mine eventually . I relaunch the code this morning and my function works. I don’t know what happened. My workspace and namespace may be too messy.
For reference, some strategies you could use when you’re not sure you will get a proper output z:
Initialise z using a sentinel value (which compute would never return, e.g. z = Float64[]). If compute was successful, z will have been rebound to a proper value. The caller of f then determines for success by comparing the returned value to the sentinel value. You might have to be a bit careful with type stability (though a Union return type with two options, such as Union{Vector{Float64}, Nothing}, should be fine).
Put z into a mutable container in which it can be undefined. E.g. z = Base.RefValue{Vector{Float64}}() and z[] = compute(x, y). Outside of f you can then check for success of z = f(x, y) via isdefined(z, :x) (where :x refers to the single field of a RefValue).
Declare, but don’t initialise z outside of the while loop. Raise an error if it has not been defined when you want to return it. You can do this by explicitly keeping track if z has been successfully computed via a success flag (i.e. what you’re doing now), use something like
@isdefined(z) && return z
error("Fail!")
or just use the UndefVarError as a sign that the computation failed.
The first option (when done properly) should be the most performant, but also the most opaque. I.e. when debugging it might not always be clear that Float64[] is problematic, while #undef and especially an error clearly are.