问题描述
rand
适用于范围:
rand(1:10)
我想使rand
与Array
一起使用,并且将所有可索引并具有length
的内容都使用:
I'd like to make rand
work with Array
, and anything that is indexable and has 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)
…但是如果我尝试这样做,我的实现堆栈会溢出,大概是因为它是完全通用的,并且与传递的所有内容都匹配,所以它最终会自行调用?
… but if I try this, my implementation stack overflows, presumably because it is completely general and matches everything passed, so it ends up calling itself?
是否可以解决此问题?我想更好地了解Julia的多态函数,而不是为此特殊的(可能很愚蠢的)函数专门知识解决问题.
Is there a way to fix this? I want to better understand Julia's polymorphic functions rather than get a fix for this particular (probably silly) function specialisation.
是否还有一个工具可以发现可用的各种实现,并调试将使用特定参数调用的实现?
Is there also a tool to discover the various implementations that are available, and debug which will be called with particular arguments?
好的,有些挖掘.这很有趣...
Okay, some digging. This is interesting…
我将开始一个新的REPL,并且:
I'll start up a fresh REPL, and:
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)
…哦,亲爱的,那是我正在谈论的递归调用和堆栈溢出.
…Oh dear, that's the recursive call and stack overflow I was talking about.
但是,请注意这一点.我杀死了朱莉娅,然后重新开始REPL.这次我import Base.Random.rand
:
But, watch this. I kill Julia and start the REPL again. This time I 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
它有效–它将我的新实现添加到所有其他实现中,并选择了正确的实现.
It works – it added my new implementation to all the others, and picked the right one.
因此,我的第一个问题的答案似乎是–它确实有效".太神奇了怎么运作的?!
So, the answer to my first question seems to be – "it just works". Which is amazing. How does that work?!
但是关于模块的问题听起来没那么有趣,为什么import Base.Random
不会引入rand
方法或给出错误,而import Base.Random.rand
却给出错误.
But there's a slightly less interesting sounding question about modules, and why import Base.Random
doesn't pull in the rand
method or give an error, but import Base.Random.rand
does.
推荐答案
方法扩展
正如某些人指出的那样,Julia让您扩展函数:您可以为不同类型的函数提供不同的功能(请参见文档的这一部分).
Method extension
As some have pointed, Julia let you extend functions: you can have functions that work differently for different types (see this part of the documentation).
例如:
f(x) = 2
f(x::Int) = x
在此示例中,我们有一个函数的版本(或方法),当(且仅当)参数的类型为Int
时,才调用该函数.第一个叫否则.
In this example, we have a version (or method) of the function that is called if (and only if) the argument is of the type Int
. The first one is called otherwise.
我们说我们已经扩展了f
函数,现在它有2种方法.
We say that we have extended the f
function, and now it has 2 methods.
那么,您想要的是扩展rand
函数.
What you want, then, is to extend the rand
function.
您希望您的rand
函数(如果使用未被其他方法捕获的参数调用)执行thing[rand(1:length(thing))]
.如果正确完成,您将调用应用于Range
对象的rand
方法,因为您将1:length(thing)
作为参数传递.
You want your rand
function, if called with a argument that was not caught by other methods, to execute thing[rand(1:length(thing))]
. If done correctly, you would call the rand
method that is applied to a Range
object, since you are passing 1:length(thing)
as argument.
尽管存在缺陷(如果thing
没有长度,例如复数?),我会说您的第一次尝试是非常合理的.
Although flawed (what if thing
doesn't have a length, e.g. a complex number?), I would say your first attempt was very reasonable.
问题:rand
无法在程序的第一个版本上扩展.根据这部分文档 ,编写import Base.Random
不能使rand
可用于方法扩展.
The problem: rand
couldn't be extended on the first version of your program. According to this piece of documentation, writing import Base.Random
doesn't make rand
available for method extension.
在尝试扩展rand
时,实际上覆盖了 rand
函数.此后,当您调用函数rand
时,只有您的方法!
While trying to extend rand
, you actually overwrite the rand
function. After this, when you call the function rand
, there is only your method!
请记住,您所依赖的事实是另外定义了范围方法(例如rand(1:10)
),并且给出了预期的结果.发生的事情是该方法被您的方法覆盖,因此再次(递归)调用了您的方法.
Remember, you were relying on the fact that a method for ranges (e.g. rand(1:10)
) was defined elsewere, and that it gave the result you expected. What happened was that this method was overwritten by yours, so your method is called again (recursively).
解决方案:导入rand
,例如扩展名.您可以在文档表格上看到它.
The solution: import rand
such as it is available to extension. You can see that on the table on the documentation.
请注意,您的第二个程序(带有import Base.Random.rand
的程序)和Colin的程序(带有importall Base.Random
的程序)正是这样做的.这就是它们起作用的原因.
Notice that your second program (the one with import Base.Random.rand
) and Colin's program (the one with importall Base.Random
) did exactly that. That's why they work.
请记住,哪些方法可以扩展,哪些方法不可用,如果文档不够清晰,将欢迎报告错误(或修复).
Keep in mind what methods are or are not available for extension, and if the documentation isn't clear enough, a bug report (or maybe a fix) will be welcomed.
这篇关于我可以写“在任何可能的情况下"工作的Julia方法吗?像C ++模板函数一样?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!