我可以编写“尽可能"有效的 I Julia 方法吗?像 C++ 模板函数? [英] Can I write I Julia method that works "whenever possible" like a c++ template function?

查看:14
本文介绍了我可以编写“尽可能"有效的 I Julia 方法吗?像 C++ 模板函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

rand 适用于范围:

rand(1:10)

我想让 randArray 以及任何可索引且具有 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆