有没有更好的方法来组合多个 `Union{T, Nothing}`
Isn't there a nicer way to combine multiple `Union{T, Nothing}`
我是 Julia 的新手,但我对 Scheme/Rust/F# 有一些了解。
今天我想 yesterday's AoC 更好,但没有明确的嵌套循环数。
我找到了这个可行的解决方案,但我不喜欢最后一个 if
。在上面提到的语言中,我会调用一个函数(或使用计算表达式),它给我第一个不是 None
的结果。对于 Julia,我希望 something 能够做到这一点。它确实,但出人意料地以一种急切的方式。
所以当我尝试 return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended))
时,当第一个参数已经有结果时,它也计算了第二个参数——因此崩溃了。
是否有我没有找到的 macro/lazy 版本或 something
?你应该如何处理这样的情况?
我也考虑过(短路)或将它们组合在一起,但我猜 Julia 在这件事上的严格性破坏了它。
using DataStructures
function find(r::Array{Int}, n, start = 1, which = nil())::Union{Int,Nothing}
if start <= length(r)
extended = cons(start, which)
with_current = sum(i -> r[i], extended)
if with_current == 2020 && n == 1
return prod(i -> r[i], extended)
else
# Unfortunately no :(
#return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended))
re = find(r, n, start + 1, which)
if isnothing(re)
return find(r, n - 1, start + 1, extended)
else
re
end
end
end
end
考虑到评论中的讨论,让我再评论一下为什么不可能。
在 Julia 中,函数参数会被急切求值,因此 Julia 会在将 find(r, n, start + 1, which)
和 find(r, n - 1, start + 1, extended)
传递给 something
函数之前对它们求值。
现在,有了宏(为了简单起见,我没有写一个完全通用的案例,我希望我的卫生是正确的:)):
julia> macro something(x, y)
quote
local vx = $(esc(x))
isnothing(vx) ? $(esc(y)) : vx
end
end
@something (macro with 1 method)
julia> @something 1 2
1
julia> @something nothing 2
2
julia> @something 1 sqrt(-1)
1
julia> @something nothing sqrt(-1)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
(在宏可变参数的完整版本中,应处理 Some
以准确复制 something
)
受到 Bogumił 的启发 我想编写我的第一个 Julia 宏。花了一些时间和无数次尝试来弄清楚语法、卫生和转义,但我现在很高兴。
我认为它可能值得分享并为 suggestions/improvements 提供机会。
懒惰的 @something
类似于 Base.something
function _something_impl(thing)
:(something($(esc(thing))))
end
function _something_impl(thing, rest...)
quote
local evalued = $(esc(thing))
if isnothing(evalued)
$(_something_impl(rest...))
else
something(evalued)
end
end
end
macro something(things...)
_something_impl(things...)
end
无异常版本
因为我发现这样的宏引发的异常不太合适,所以我还制作了一个回退到 nothing
的版本。
function _something_nothing_impl(thing)
quote
local evaluated = $(esc(thing))
if isa(evaluated, Some)
evaluated.value
else
evaluated
end
end
end
function _something_nothing_impl(thing, rest...)
quote
local evalued = $(esc(thing))
if isnothing(evalued)
$(_something_nothing_impl(rest...))
else
something(evalued)
end
end
end
macro something_nothing(things...)
_something_nothing_impl(things...)
end
现在我猜递归中间函数可以也由宏生成。 :)
我是 Julia 的新手,但我对 Scheme/Rust/F# 有一些了解。
今天我想 yesterday's AoC 更好,但没有明确的嵌套循环数。
我找到了这个可行的解决方案,但我不喜欢最后一个 if
。在上面提到的语言中,我会调用一个函数(或使用计算表达式),它给我第一个不是 None
的结果。对于 Julia,我希望 something 能够做到这一点。它确实,但出人意料地以一种急切的方式。
所以当我尝试 return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended))
时,当第一个参数已经有结果时,它也计算了第二个参数——因此崩溃了。
是否有我没有找到的 macro/lazy 版本或 something
?你应该如何处理这样的情况?
我也考虑过(短路)或将它们组合在一起,但我猜 Julia 在这件事上的严格性破坏了它。
using DataStructures
function find(r::Array{Int}, n, start = 1, which = nil())::Union{Int,Nothing}
if start <= length(r)
extended = cons(start, which)
with_current = sum(i -> r[i], extended)
if with_current == 2020 && n == 1
return prod(i -> r[i], extended)
else
# Unfortunately no :(
#return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended))
re = find(r, n, start + 1, which)
if isnothing(re)
return find(r, n - 1, start + 1, extended)
else
re
end
end
end
end
考虑到评论中的讨论,让我再评论一下为什么不可能。
在 Julia 中,函数参数会被急切求值,因此 Julia 会在将 find(r, n, start + 1, which)
和 find(r, n - 1, start + 1, extended)
传递给 something
函数之前对它们求值。
现在,有了宏(为了简单起见,我没有写一个完全通用的案例,我希望我的卫生是正确的:)):
julia> macro something(x, y)
quote
local vx = $(esc(x))
isnothing(vx) ? $(esc(y)) : vx
end
end
@something (macro with 1 method)
julia> @something 1 2
1
julia> @something nothing 2
2
julia> @something 1 sqrt(-1)
1
julia> @something nothing sqrt(-1)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
(在宏可变参数的完整版本中,应处理 Some
以准确复制 something
)
受到 Bogumił 的启发
我认为它可能值得分享并为 suggestions/improvements 提供机会。
懒惰的 @something
类似于 Base.something
function _something_impl(thing)
:(something($(esc(thing))))
end
function _something_impl(thing, rest...)
quote
local evalued = $(esc(thing))
if isnothing(evalued)
$(_something_impl(rest...))
else
something(evalued)
end
end
end
macro something(things...)
_something_impl(things...)
end
无异常版本
因为我发现这样的宏引发的异常不太合适,所以我还制作了一个回退到 nothing
的版本。
function _something_nothing_impl(thing)
quote
local evaluated = $(esc(thing))
if isa(evaluated, Some)
evaluated.value
else
evaluated
end
end
end
function _something_nothing_impl(thing, rest...)
quote
local evalued = $(esc(thing))
if isnothing(evalued)
$(_something_nothing_impl(rest...))
else
something(evalued)
end
end
end
macro something_nothing(things...)
_something_nothing_impl(things...)
end
现在我猜递归中间函数可以也由宏生成。 :)