我可以写一个有效的Julia方法"尽可能"像一个c ++模板函数?

时间:2015-01-28 09:35:47

标签: polymorphism julia

rand适用于范围:

rand(1:10)

我想rand使用Array,以及任何可转换且length的内容:

import Base.Random
rand(thing) = thing[rand(1:length(thing))]

array = {1, 2, 3}
myRand(array)

range = 1:8
myRand(range)

tupple = (1, 2, 3, "a", "b", "c")
myRand(tupple)

...但是如果我尝试这个,我的实现堆栈会溢出,大概是因为它完全是通用的并且匹配所有传递的东西,所以它最终调用自己?

有没有办法解决这个问题?我想更好地理解Julia的多态函数,而不是修复这个特殊的(可能是愚蠢的)函数特化。

是否还有一个工具可以发现可用的各种实现,以及将使用特定参数调用的调试?


好的,有些挖掘。这很有意思......

我将启动一个新的REPL,并且:

julia> import Base.Random

julia> rand(thing) = thing[rand(1:length(thing))]
rand (generic function with 1 method)

julia> rand({1,2,3})
ERROR: stack overflow
 in rand at none:1 (repeats 80000 times)

...哦亲爱的,那是我正在谈论的递归调用和堆栈溢出。

但是,请注意这一点。我杀了Julia并再次启动REPL。这次我import Base.Random.rand

julia> import Base.Random.rand

julia> rand(thing) = thing[rand(1:length(thing))]
rand (generic function with 33 methods)

julia> rand({1,2,3})
3

它有效 - 它将我的新实现添加到所有其他实现,并选择了正确的实现。

所以,我的第一个问题的答案似乎是 - “它只是有效”。哪个太棒了这有什么作用?!

但是关于模块的问题听起来有点不那么有趣,为什么import Base.Random没有引入rand方法或给出错误,但是import Base.Random.rand呢。

3 个答案:

答案 0 :(得分:4)

方法扩展

正如一些人所指出的,Julia让你扩展函数:你可以使用不同类型的函数(参见this part of the documentation)。

例如:

f(x) = 2
f(x::Int) = x

在此示例中,我们有一个函数的版本(或方法),如果(且仅当)参数的类型为Int,则调用该函数。第一个被称为其他。

我们说我们扩展了f功能,现在它有2种方法。

您的问题

然后,您想要扩展rand功能。

如果使用未被其他方法捕获的参数调用,您希望rand函数执行thing[rand(1:length(thing))]。如果正确完成,您将调用应用于rand对象的Range方法,因为您将1:length(thing)作为参数传递。

虽然存在缺陷(如果thing没有长度,例如复数?),我会说你的第一次尝试是非常合理的。

问题rand无法在程序的第一个版本上进行扩展。根据{{​​3}},撰写import Base.Random不会使rand可用于方法扩展。

在尝试扩展rand时,您实际上覆盖 rand功能。在此之后,当您调用函数rand时,只有您的方法!

请记住,你依赖于一个范围(例如rand(1:10))的方法被定义的事实,并且它给出了你期望的结果。发生的事情是这个方法被你的方法覆盖了,所以再次调用你的方法(递归)。

解决方案:导入rand等可用于扩展。您可以在文档的表格中看到它。

请注意,您的第二个程序(具有import Base.Random.rand的程序)和Colin的程序(具有importall Base.Random的程序)完全相同。这就是他们工作的原因。

请记住哪些方法可用于或不可用于扩展,如果文档不够清晰,则欢迎使用错误报告(或修复)。

答案 1 :(得分:2)

我打算将此作为评论,但结果太长了:

请注意另外两种方法,这些方法也将工作":

Base.rand(thing) = thing[rand(1:length(thing))]
rand({1,2,3})

或者您也可以使用:

importall Base.Random
rand(thing) = thing[rand(1:length(thing))]
rand({1,2,3})

当您使用import Base.Random时,实际上不允许您使用本地定义的Base.rand扩展rand。这是因为import语句只能用于函数,而不能用于模块。如果要导入模块中的所有函数,则需要使用importall(如上例所示)。或者(如上所述),您可以直接在函数定义Base.rand中引用该模块。

不可否认,上次查看文档时,这一点肯定会更加明确。它可能会在最新版本中解决。

关于你的其余问题(为什么它和#34;只是工作"?),我不相信我能给出一个明智的答案,所以我可能会把它留给其他人。

另外,partial duplicate

答案 2 :(得分:2)

正如另一个指出的那样,明确指定Base.rand=而不是rand=会在Base中为rand函数定义添加一个方法,而不是完全替换它。

例如,

julia> Base.rand(x::Any)=x[rand(1:length(x)]
rand (generic function with 33 methods)

这种作用的原因与朱莉娅的abstract type system有关。当Julia正在寻找具有匹配方法签名的函数时,它从定义类型的叶节点开始(例如,x是否是Int8),然后向上移动类型层次结构,直到找到具有匹配的函数签名。在类型层次结构的顶部是catchall类型Any。如果层次结构的任何级别都没有匹配的函数,则函数调用将失败。

这是一个简单的例子。我们将创建一个函数f,它响应类型层次结构中的几个不同类型,其类型为:

julia> f(x::Int)="Int"
f (generic function with 1 method)

julia> f(x::Real)="Real"
f (generic function with 2 methods)

julia> f(x::Number)="Number"
f (generic function with 3 methods)

julia> f(x::Any)="Any"
f (generic function with 4 methods)

julia> f(x::Array)="Array"
f (generic function with 5 methods)

julia> f(4) # typeof(4) isInt64
"Int"

julia> f(2.0) # typeof(2.0) is Float64
"Real"

julia> f(3im) # typeof(3im) is Complex{Int64}

"Number"

julia> f([1,2]) # typeof([1,2]) is Array{Int64, 1}
"Array"

julia> f(Dict(1=>3,4=>5)) # typeof(Dict(1=>3,4=>5)) is Dict{Int64, Int64}
"Any"