Julia函数返回匿名函数

8

我正在尝试理解一段相对简单的代码,但我并不能完全理解正在发生什么(我是一个Julia新手,来自Python / Matlab背景)。

function myfunc(number::Integer)
    double() = 2*number
    square() = number^2
    return _ -> (number, double, square)
end

我理解myfunc会返回一个匿名函数,该函数不关心传递给它的值。因此,以下情况对我来说是有意义的:

julia> n4 = myfunc(4)
#9 (generic function with 1 method)

julia> n4(50)
(4, var"#double#10"{Int64}(4), var"#square#11"{Int64}(4))

在第一行中,n4 指的是匿名函数本身,而在第二行中,使用参数 50 调用了该匿名函数并完成了它应该完成的任务:丢弃 50 并返回包含定义数据的元组。 我不理解的是我如何能够执行以下操作:
julia> n4.square
(::var"#square#11"{Int64}) (generic function with 1 method)

julia> n4.square()
16
n4代表一个匿名函数,但它拥有n4.numbern4.doublen4.square等子对象,对此感到惊讶。为什么n4的行为表现得像一个结构体呢?执行n4(*)[2]()以获得8作为答案是有意义的,但当fieldnames(n4)失败时,我不理解在背后发生了什么使n4.double()能够工作。我能够使用n4之后的.来获取函数/数据,这种机制是如何运作的?

https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects-1 - carstenbauer
看一下 Meta.@lower 函数 myfunc()...。如果你习惯于阅读降低后的代码,那么这将告诉你关于细节方面的几乎一切内容。 - phipsgabler
1个回答

8
在Julia中,所有的通用函数(也就是所有常规定义的Julia函数)都是结构体。在Julia中,任何结构体都可以被调用,因此默认情况下,普通函数只是一个可调用的零字段结构体(单例)。换句话说,执行以下操作: ``` foo(x) = x+1 ``` 类似于
struct Foo end
const foo = Foo()
(::Foo)(x) = x + 1

这对于普通函数非常有效,但对于匿名函数,这可能会导致创建大量新类型。例如,您可以执行以下操作: functions = [x -> x+i for i in 1:1000]

Julia在这里创建了一个包含i字段和1000个实例的单个类型,而不是创建1000个具有新值的新类型i

在您的情况下,myfunc不会为每次调用返回新的类型并返回该类型的单例实例,而是返回一个带有字段numberdoublesquare的类型的实例。

此外,您完全可以调用fieldnames,只需要调用它并提供类型:fieldnames(typeof(myfunc(4)))

这仅仅是一种优化方法,感觉有点奇怪,因为您的代码依赖于如何在内存中表示函数的内部结构。您可能不应该那样做。


谢谢你的回答Jakob。我可能需要一些时间才能完全理解。在此期间,我可以问一个跟进问题吗?如果所有函数都是结构体,那么我可以公开局部变量吗?比如我有一个function test(x) var = x+4; z = var * var end,是否有可能以某种方式访问var?我认为我们不应该能够这样做,如果可能的话,这可能不被推荐 - 但我只是为了学习而问。 - ITA
只需将其作为附加参数返回:test(x) = let var = x+4, z = var*var; var, z; end - phipsgabler
1
不,那是不可能的。因为编译过程可以自由地删除变量。例如,如果您执行 f(x) = (a = 1; 2),则无法访问 a,因为它从未被实例化。 - Jakob Nissen
@phipsgabler 我的意思是以 test.varsomebasefunc(test).var 的格式/样式,但从Jakob的回复中我得出结论这是不可能的。 - ITA
啊,但你可以为此定义一个可调用的结构体。关于这个问题提一个新的问题,评论太小了,无法写出可读性强的内容。 - phipsgabler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接