我可以编写“尽可能"有效的 I Julia 方法吗?像 C++ 模板函数? [英] Can I write I Julia method that works "whenever possible" like a c++ template function?
问题描述
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.
但是,看这个.我杀死 Julia 并再次启动 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)
)被定义为 elsewere,并且它给出了您预期的结果.发生的事情是这个方法被你的覆盖了,所以你的方法被再次调用(递归).
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.
这篇关于我可以编写“尽可能"有效的 I Julia 方法吗?像 C++ 模板函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!