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
呢。
答案 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;只是工作"?),我不相信我能给出一个明智的答案,所以我可能会把它留给其他人。
答案 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"