Methods 如何指定函数';Julia中的s类型/签名?

Methods 如何指定函数';Julia中的s类型/签名?,methods,types,julia,signature,Methods,Types,Julia,Signature,我正在一个预编译库中实现。常见的情况是使用从Float64到Float64的函数,我希望库中存在它的优化编译版本。当然,我也将实现一个类型泛型版本,但是Julia需要通过签名来区分方法,以便知道在运行时调用哪个方法。目前的实施是: #Float64 optimized version function findrootNewton( func, funcder, guess::Float64, rtol::Float64=1e-12, abstol

我正在一个预编译库中实现。常见的情况是使用从
Float64
Float64
的函数,我希望库中存在它的优化编译版本。当然,我也将实现一个类型泛型版本,但是Julia需要通过签名来区分方法,以便知道在运行时调用哪个方法。目前的实施是:

#Float64 optimized version
function findrootNewton( func, funcder, guess::Float64,
                        rtol::Float64=1e-12, abstol::Float64=1e-12, maxiter::Int=100 )
    #make sure tolerances are positive
    if rtol <= 0.0
        error("findrootNewton: rtol must be a positive number")
    end

    if abstol <= 0.0
        error("findrootNewton: abstol must be a positive number")
    end

    if maxiter <= 0.0
        error("findrootNewton: maxiter must be a positive integer")
    end

    converged::Bool = false

    oldx::Float64 = guess
    newx::Float64 = oldx - ((func(oldx) / funcder(oldx))::Float64)
    absdiff = abs(oldx - newx)

    iter = 2
    while (absdiff < abstol || absdiff < rtol * abs(newx)) && iter <= maxiter
        oldx = newx
        newx = oldx - func(oldx) / funcder(oldx)
        absdiff = abs(oldx - newx)

        iter += 1
    end #while (absdiff < abstol || absdiff < rtol * abs(newx)) && newxiter <= maxiter

    if iter <= maxiter
        converged = true
    end

    return (newx, converged)
end #findzeroNewton

#Generic version
function findrootNewton( func, funcder, guess::Number,
                        rtol::Real=1e-12, abstol::Real=1e-12, maxiter::Int=100 )
    #make sure tolerances are positive
    if rtol <= 0
        error("findrootNewton: rtol must be a positive number")
    end

    if abstol <= 0
        error("findrootNewton: abstol must be a positive number")
    end

    if maxiter <= 0
        error("findrootNewton: maxiter must be a positive integer")
    end

    converged::Bool = false

    newx = oldx - func(oldx) / funcder(oldx)
    oldx = convert(typeof(newx), guess)
    absdiff = abs(oldx - newx)

    iter = 2
    while (absdiff < abstol || absdiff < rtol * abs(newx)) && iter <= maxiter
        oldx = newx
        newx = oldx - func(oldx) / funcder(oldx)
        absdiff = abs(oldx - newx)

        iter += 1
    end #while (absdiff < abstol || absdiff < rtol * abs(newx)) && newxiter <= maxiter

    if iter <= maxiter
        converged = true
    end

    return (newx, converged)
end #findzeroNewton

是否可以类似地指定可以存储(指向)函数的变量的签名?

为清楚起见,我将写下我的注释作为答案: 您不需要在Julia中专门化类型上的函数签名,除非它是为了在函数体中实现专门化处理。参数类型断言对代码速度或可编译性没有影响。看

julia中函数参数中的类型断言主要用于控制多个分派,即不同类型的输入参数的不同函数行为。当不断言类型时,编译器将自动为每个输入参数组合编译一个类型专用版本

如果您出于其他原因(例如,为了确保类型稳定性)需要断言函数的返回类型与输入相同,您可以这样做

function foo(x::T)::T where T
...
end

这不是编写Julia代码的方法。你在写Julia,就好像它是一种静态类型的语言。这是一个容易犯的错误,因为Julia“看起来”很像一种静态类型的语言。要在Julia中获得性能,重要的不是使用类型进行注释,而是实现类型稳定性

这意味着编写代码,以便在执行一段代码时,变量的类型不会改变。Julia为您提供了许多功能和便利,如
zero
iszero
类似的

只需使用泛型函数,它将具有与“专用”函数相同的性能。Julia中特定类型的专用函数仅在需要不同算法时才适用。例如,
相交(圆,三角形)
需要与
相交(圆,圆)
不同的代码。但是,对于使用32位浮点数的圆和使用64位浮点数的圆,您不会编写不同的方法


为了给出一些具体的建议,让我对您编写的一些代码进行评论

 if rtol <= 0.0
        error("findrootNewton: rtol must be a positive number")
 end
这是不必要的,也是错误的思考方式。记住Julia不是一种静态类型的语言。如果您是一名C/C++开发人员,您可能会将变量视为大小不同的小内存盒,它可以容纳浮点、整数或布尔值。然后,此赋值意味着将2.5放入64位浮点框中

然而,在像Julia这样的动态语言中,情况并非如此。从概念上讲,创建一个浮点对象
2.5
,并在其上粘贴一个标签
x
。作业在某种程度上与你的想法相反。您没有为名为
x
的框指定数字。而是将标签
x
粘贴到数字
2.5
上。所以写一些东西,比如:

converged::Bool = false
完全没有必要。您无法确保将
false
放入布尔大小的框中。没有盒子。而是将标签
聚合
粘贴到
false
对象上,然后断言标签
聚合
附加到布尔对象。这不会给您带来性能或内存优势


您只在通用版本中执行这一行,并且可以假定这比您在Float64版本中执行的性能要差

oldx = convert(typeof(newx), guess)
但是,调用
convert
typeof
没有任何区别。在所有类型都匹配的理想情况下,这些调用会得到优化。看看这个简单的例子:

julia> foobar(a, b) = convert(typeof(a), a + b)
foobar

julia> @code_warntype foobar(1, 1)
Body::Int64
1 ─ %1 = (Base.add_int)(a, b)::Int64
└──      return %1

julia> @code_warntype foobar(1.0, 1.0)
Body::Float64
1 ─ %1 = (Base.add_float)(a, b)::Float64
└──      return %1
可以看到,当类型匹配时,juliajit将其简化为简单的整数或浮点加法

如果您不确定以这种或那种方式编写函数对性能的影响,我建议您习惯使用
@code\u warntype
@code\u llvm
@code\u native
宏。它们为您提供了关于Julia如何在给定特定参数集的情况下转换代码的有价值的信息

至于您关于是否可以为函数签名创建类型断言的问题。你现在不能在朱莉娅身上这样做。在你的情况下,它是不需要的


然而,解决方法通常涉及使用一些基于特征的方法。您可以考虑将牛顿方法的参数转换为一种类型,然后将其输入调度。p> 您可以使用
Float64(…表达式返回BigFloat…)
将BigFloat转换为Float64,而不是类型断言。此外,在Julia中,通常建议从较少的类型开始,因为类型通常来自编译时类型推断,并在运行时动态进行。@DanGetz这就是泛型案例的目的,但我始终注意“使通用案例快速”这在Julia中是不可能的吗?类型断言检查变量的类型,如果它不是某个类型的子类型,则抛出错误<代码>转换(…)或构造函数尝试将一种类型转换为另一种类型。我想你可能想要后者,但也许我误解了这个问题。显式键入不能让函数在Julia中运行得更快。编译器自动专门处理所有输入参数的通用情况。唯一的例外是,如果植入体是非常不同的-但我很难理解为什么它会是浮动64。但它可以!这就是julia的优点——它不是编译一个通用版本,而是为每一组参数编译一个专门的版本。事实上,这是一项关键的创新
oldx = convert(typeof(newx), guess)
julia> foobar(a, b) = convert(typeof(a), a + b)
foobar

julia> @code_warntype foobar(1, 1)
Body::Int64
1 ─ %1 = (Base.add_int)(a, b)::Int64
└──      return %1

julia> @code_warntype foobar(1.0, 1.0)
Body::Float64
1 ─ %1 = (Base.add_float)(a, b)::Float64
└──      return %1