I don’t quite get why these broadcasting operations do not work:
julia> x = rand(3); y = rand(3);
julia> f(x,y) = @. (x-y > 0.5 ? x : y )
f (generic function with 1 method)
julia> f(x,y)
ERROR: TypeError: non-boolean (BitVector) used in boolean context
Stacktrace:
[1] f(x::Vector{Float64}, y::Vector{Float64})
@ Main ./REPL[38]:1
[2] top-level scope
@ REPL[39]:1
Maaaaaybe. It’s a little stranger than .&& and .||. It (currently) is syntacticly identical to the if statement, and we don’t support if. or .if. Are a .? b : c and a ? b .: c errors? We also don’t support [1,2,3] .: [8,9,10], and I think there’s strange parsing ambiguities around .: in general.
It seems that I have to add a dot to the minus there:
julia> f(x,y) = ifelse.(x - y > 0.5, x, y)
f (generic function with 1 method)
julia> f(x,y)
ERROR: MethodError: no method matching isless(::Float64, ::SVector{3, Float64})
while
julia> f(x,y) = ifelse.(x - y .> 0.5, x, y)
f (generic function with 1 method)
julia> f(x,y)
3-element SVector{3, Float64} with indices SOneTo(3):
-0.636700731442766
0.16919692261464525
-0.3143986886849526
julia> x = @SVector(rand(3)); y = @SVector(rand(3)); period = @SVector(rand(3));
julia> function getdx(x,y,period)
delta = x - y
ifelse.(delta .> 0.5 .* period ? delta - period : delta) .* sign.(x-y)
end
getdx (generic function with 1 method)
julia> getdx(x,y,period)
ERROR: TypeError: non-boolean (SVector{3, Bool}) used in boolean context
I can’t find the possible combination of dots and not-dots or @. that makes this work as a one-liner.
If you want a macro that does this for you, you could always just do something like
julia> using MLStyle
julia> macro (..)(ex)
f = @λ begin
:($a ? $b : $c) => :($Base.ifelse($(f(a)), $(f(b)), $(f(c))))
x => x
end
esc(:($Base.@. $(f(ex))))
end
@.. (macro with 1 method)
julia> let x = rand(3), y = rand(3);
@.. (x-y > 0.5 ? x : y )
end
3-element Vector{Float64}:
0.18240422481232943
0.8391572489988877
0.43624514306257667
This works correctly on nested expressions:
julia> let x = rand(3), y = rand(3);
@.. (x-y > 0.5 ? (x > y ? y : x) : y)
end
3-element Vector{Float64}:
0.7793973727797479
0.3033699172383637
0.625107294185069
But won’t this macro always evaluate both branches?
You can write something like this:
broadcast(xs,ys) do x,y
x-y>0.5 ? x : y
end
where in reality, surely the expressions will be a bit longer: If you don’t want ifelse, there must be some work you’d like not to happen when the condition is not met, so it can’t just be one symbol.
You can also write g(x,y) = @cast _[i] := x[i]-y[i] > 0.5 ? x[i] : y[i] using TensorCast, after someone complained that it didn’t work…
Whereas a ? b : c parses directly to an :if head. Like I said, just little things all around that can change, but the above is what I was referencing. The fact that a && b has its own special parsed form made it significantly easier to achieve a .&& b.