Julia 如何扩展 isless 函数
Julia how to extend isless function
我是 Julia 的新手,刚刚了解到 Julia 可以通过更改函数签名来生成泛型方法。
function round_number(x::Float64)
return round(x)
end
round_number(3.1415)
round_number(3)
function round_number(x::Int64)
return x
end
round_number(3)
当我发现函数 test_interpolated
无法处理数字和字符串时,我尝试查看是否可以生成新签名来扩展 isless
。不幸的是,它没有用。
function test_interpolated(a, b)
if a < b
"$a is less than $b"
elseif a > b
"$a is greater than $b"
else
"$a is equal to $b"
end
end
test_interpolated(3.14, 3.14)
test_interpolated(3.14, "3.14")
function isless(x::AbstractFloat, y::AbstractString)
return x < float64(y)
end
test_interpolated(3.14, "3.14")
我是不是理解错了这个概念?
首先请注意,Julia 中没有 float64
函数。要将字符串解析为数字,您需要使用 parse
函数:
parse(Float64, "3.14")
您代码中的第二件事是:
function isless(x::AbstractFloat, y::AbstractString)
return x < float64(y)
end
在您当前的模块中创建一个名为 isless
的 新函数(如果您正在使用 REPL,则很可能 Main
)。
要向 Base
模块中定义的 isless
函数添加方法,您必须编写:
function Base.isless(x::AbstractFloat, y::AbstractString)
return x < float64(y)
end
(注意 Base.
前缀)
尽管 Julia 允许这样做,但您永远不应在代码中这样做。这种风格叫做盗版,解释here.
简而言之,您正在向 Base
模块中的函数添加方法,并且 AbstractFloat
和 AbstractString
也在 Base
模块中定义。问题是添加这样的方法可能会破坏其他代码,这些代码假设在这种情况下 isless
在传递 AbstractFloat
和 AbstractString
时会抛出错误。
你应该定义你的函数的方式是例如如下:
toreal(x::Real) = x
toreal(x::AbstractString) = parse(Float64, x)
function test_interpolated(a::Real, b::Real)
if a < b
"$a is less than $b"
elseif a > b
"$a is greater than $b"
else
"$a is equal to $b"
end
end
test_interpolated(a, b) = test_interpolated(toreal(a), toreal(b))
通过这种方式,您可以确保您有一个具有窄签名的特定方法实现实数的逻辑,而另一个 test_interpolated
方法具有宽签名,可以执行到适当类型的转换。
请注意,我使用 Real
而不是 AbstractFloat
,因为与 <
的比较保证在两个 Real
值之间定义,并且您很可能希望您的函数例如接受整数。
对我展示的方法进行了解释 here。
编辑
调度歧义的例子。使用全新的 Julia 1.7.0 会话:
julia> struct T end
julia> Base.propertynames(::T, ::Integer) = nothing
julia> propertynames(T(), true)
ERROR: MethodError: propertynames(::T, ::Bool) is ambiguous. Candidates:
propertynames(x, private::Bool) in Base at reflection.jl:1666
propertynames(::T, ::Integer) in Main at REPL[2]:1
我是 Julia 的新手,刚刚了解到 Julia 可以通过更改函数签名来生成泛型方法。
function round_number(x::Float64)
return round(x)
end
round_number(3.1415)
round_number(3)
function round_number(x::Int64)
return x
end
round_number(3)
当我发现函数 test_interpolated
无法处理数字和字符串时,我尝试查看是否可以生成新签名来扩展 isless
。不幸的是,它没有用。
function test_interpolated(a, b)
if a < b
"$a is less than $b"
elseif a > b
"$a is greater than $b"
else
"$a is equal to $b"
end
end
test_interpolated(3.14, 3.14)
test_interpolated(3.14, "3.14")
function isless(x::AbstractFloat, y::AbstractString)
return x < float64(y)
end
test_interpolated(3.14, "3.14")
我是不是理解错了这个概念?
首先请注意,Julia 中没有 float64
函数。要将字符串解析为数字,您需要使用 parse
函数:
parse(Float64, "3.14")
您代码中的第二件事是:
function isless(x::AbstractFloat, y::AbstractString)
return x < float64(y)
end
在您当前的模块中创建一个名为 isless
的 新函数(如果您正在使用 REPL,则很可能 Main
)。
要向 Base
模块中定义的 isless
函数添加方法,您必须编写:
function Base.isless(x::AbstractFloat, y::AbstractString)
return x < float64(y)
end
(注意 Base.
前缀)
尽管 Julia 允许这样做,但您永远不应在代码中这样做。这种风格叫做盗版,解释here.
简而言之,您正在向 Base
模块中的函数添加方法,并且 AbstractFloat
和 AbstractString
也在 Base
模块中定义。问题是添加这样的方法可能会破坏其他代码,这些代码假设在这种情况下 isless
在传递 AbstractFloat
和 AbstractString
时会抛出错误。
你应该定义你的函数的方式是例如如下:
toreal(x::Real) = x
toreal(x::AbstractString) = parse(Float64, x)
function test_interpolated(a::Real, b::Real)
if a < b
"$a is less than $b"
elseif a > b
"$a is greater than $b"
else
"$a is equal to $b"
end
end
test_interpolated(a, b) = test_interpolated(toreal(a), toreal(b))
通过这种方式,您可以确保您有一个具有窄签名的特定方法实现实数的逻辑,而另一个 test_interpolated
方法具有宽签名,可以执行到适当类型的转换。
请注意,我使用 Real
而不是 AbstractFloat
,因为与 <
的比较保证在两个 Real
值之间定义,并且您很可能希望您的函数例如接受整数。
对我展示的方法进行了解释 here。
编辑
调度歧义的例子。使用全新的 Julia 1.7.0 会话:
julia> struct T end
julia> Base.propertynames(::T, ::Integer) = nothing
julia> propertynames(T(), true)
ERROR: MethodError: propertynames(::T, ::Bool) is ambiguous. Candidates:
propertynames(x, private::Bool) in Base at reflection.jl:1666
propertynames(::T, ::Integer) in Main at REPL[2]:1