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 模块中的函数添加方法,并且 AbstractFloatAbstractString 也在 Base 模块中定义。问题是添加这样的方法可能会破坏其他代码,这些代码假设在这种情况下 isless 在传递 AbstractFloatAbstractString 时会抛出错误。

你应该定义你的函数的方式是例如如下:

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