Use condition result inside conditional

I find myself with some frequency having to test a condition and, if that condition returns some value, do something with that value. For example:

x = [ 1, 2, 3 ]
itest = findfirst(isequal(2),x) # get value
if itest != nothing # test condition
  deleteat!(x,itest) # use value
end

This can be done without defining test outside the conditional, but calling findfirst twice:

if findfirst(isequal(2),x) != nothing  # test condition
  i = findfirst(isequal(2),x) # get value again
  deleteat!(x,i) # use value
end

Is there a way to use a more clean syntax as in the second case, but without repeating the findfirst command?

In particular, this becomes useful if I want to add other associated conditionals:

x = [ 1, 2, 3 ]
if findfirst(isequal(2),x) != nothing
  i = findfirst(isequal(2),x)
  deleteat!(x,i)
elseif findfirst(isequal(3),x) != nothing
  i = findfirst(isequal(3),x)
  deleteat!(x,i)
end

The alternative here is to split the conditional into independent if end blocks, when that is the same, with temporary itest variables before each one:

itest = findfirst(isequal(2),x)
if itest != nothing
  deleteat!(x,itest)
end
itest = findfirst(isequal(3),x)
if itest != nothing
  deleteat!(x,itest)
end

For a one-liner, I arrived to this:

 i = findfirst(isequal(2),x); i != nothing && deleteat!(x,i)
 i = findfirst(isequal(3),x); i != nothing && deleteat!(x,i)

Is there a cleaner way to write this? Particularly in the case of elseif statements, which are not necessarily the same thing as the multiple if end blocks.

1 Like

The following works:

foo() = 3

if (x = foo()) !== nothing
    println(x)
end

This works because in Julia, assignment is an expression, not just a statement.

4 Likes

Any particular reason here for using !== instead of != ?

Actually isnothing would have been a better choice on my part.

===/!== is sometimes held to be slightly better/more efficient when it’s possible to use it, see for example.

3 Likes

@patrick-kidger, how would you use isnothing() in this situation? Thanks in advance.

The following works:

foo() = 3

if !isnothing(begin x = foo() end)
    println(x)
end

To be honest I’d probably stick with using !== for readability. It’s not really important either way.

3 Likes

Thanks, didn’t know one could do this kind of stuff, but it looks a bit twisted.

1 Like

Yeah, I sometimes wish we had something like the if-let or when-let macros that exist in some LISP-y languages.

Defining one in Julia could look something like this:

# WARNING: proof of concept only!
macro when_let(assignment, body)
    var = assignment.args[1]
    quote
        let $assignment
            if $var !== nothing
                $body
            end
        end
    end |> esc
end
julia> x = [1, 2, 3];

julia> @when_let i = findfirst(isequal(2), x) begin
           deleteat!(x, i)
       end
2-element Array{Int64,1}:
 1
 3

One thing that bothers me about such a macro is that it hard-codes the comparison against nothing. This is OK for LISPs, where nil is used as the false boolean anyway. This is also OK in Julia for functions like findfirst or match, which return nothing as a base case. But I still feel like a when-let macro would be more “Julian” if it allowed specifying an arbitrary predicate. I’ve never found a good, readable syntax for this, though…

4 Likes

One used to be able to do this kind of stuff, back when everyone learnt C in the course of learning to program. Nowadays, it’s best avoided, because the kids all think you’ve stuffed up by using = where you meant ==, and file bug reports. :wink:

2 Likes