Julia 如何编写高阶函数以向给定函数添加方法?
假设我想写一个接受任何关联运算符的函数⊕ 并向其添加方法,以便我可以用函数替换任何值。这些附加方法的语义如下所示:Julia 如何编写高阶函数以向给定函数添加方法?,julia,Julia,假设我想写一个接受任何关联运算符的函数⊕ 并向其添加方法,以便我可以用函数替换任何值。这些附加方法的语义如下所示: 如果运算符随后应用于任何两个函数f和g,则结果应该是一个函数,该函数首先将f和g(独立)应用于其参数,然后再应用⊕ 结果如何 如果一个参数是函数f,但另一个是任何非函数值x,则结果是一个函数,该函数首先将f应用于其参数,然后再应用⊕ 到结果和x 我可以用代码表示为: associative!(⊕) = begin ⊕(f::F, y) where F<:Funct
- 如果运算符随后应用于任何两个函数
和f
,则结果应该是一个函数,该函数首先将g
和f
(独立)应用于其参数,然后再应用⊕ 结果如何g
- 如果一个参数是函数
,但另一个是任何非函数值f
,则结果是一个函数,该函数首先将x
应用于其参数,然后再应用⊕ 到结果和f
x
associative!(⊕) = begin
⊕(f::F, y) where F<:Function = (xs...) -> f(xs...) ⊕ y
⊕(x, g::G) where G<:Function = (xs...) -> x ⊕ g(xs...)
⊕(f::F, g::G) where {F<:Function, G<:Function} = (args...) -> f(args...) ⊕ g(args...)
⊕(f::F, y, zs...) where F<:Function = f ⊕ ⊕(y, zs...)
⊕(x, g::G, zs...) where G<:Function = x ⊕ ⊕(g, zs...)
⊕(f::F, g::G, zs...) where {F<:Function, G<:Function} = f ⊕ ⊕(g, zs...)
end
我知道我可以编写一个高阶函数,返回一个基于给定函数/运算符的新函数/运算符。例如:
associative(⊞) = begin
let ⊕(x) = x
⊕(x, y) = x ⊞ y
⊕(x, y, zs...) = ⊕(x⊞y, zs...)
associative!(⊕)
⊕
end
end
如果我内联关联的定义代码>在这里<代码>关联
工作正常,我可以写:
⊕ = associative(+)
⊗ = associative(*)
f(x) = 3⊗x ⊕ 1
f(1) # 4
f(cos)(0) # 4
但我觉得有一个变异版本也不错。我假设您正在重新编写
associate
作为宏可以工作,但是这里似乎没有任何需要使用宏的东西。那么,作为一个高阶函数,有可能做到这一点吗?如果有,怎么做?我不知道如何写符号,但这难道不能让你朝着你想要的方向前进吗
import Base.+
+(f::Function, g::Function) = x -> f(x) + g(x)
+(f::Function, x) = f(x) + x
# e.g.
new_plus = +(sum, sum)
new_plus([1,2,3]) # gives 12
这里发生了一些事情。首先要注意的是
⊕(,我们看到了。为了向函数别名动态添加方法,我们基本上需要使用与为某些任意数据类型定义可调用对象相同的语法:
(::typeof(⊕))(<args...>) = <body...>
在同一个线程中,我们看到这是由于Julia 0.6之后的某个时间发生了更改,因此我们需要使用这些表达式。现在我们涉及到@eval
,我们需要记住以下几点:
- 我们需要将我们的引用拼接到
⊕代码>使用$⊕代码>
⊕
似乎会干扰Julia将其识别为中缀运算符。因此我们需要使用函数(前缀)表示法
综上所述,我们最终得出如下结论:
@eval (::typeof($⊕))(f::F, y) where F<:Function = (xs...) -> $⊕(f(xs...), y)
是的,这基本上是手动完成的,但是,对于单个操作符(+
)。对于*
等,您必须再次重复整个过程。但无论如何,问题不在于此特定情况。可以想到自动向给定函数添加方法的其他用途。我只是想知道是否可以使用高阶函数或其他方法来完成此操作(例如宏)是必需的。我明白了。我认为那时需要宏,因为在这个中(⊕) = 开始⊕(f::f,y)其中f(xs…)⊕ Y⊕(x,g::g)其中gx⊕ g(xs…)end
函数仅在关联函数的范围内定义,但我认为您希望在全局范围内定义它,在这种情况下,您需要一个宏。
Global method definition around ... needs to be placed at the top level, or use "eval"
@eval (::typeof($⊕))(f::F, y) where F<:Function = (xs...) -> $⊕(f(xs...), y)
@eval let ⊕ = $⊕
(::typeof($⊕))(f::F, y) where F<:Function = (xs...) -> f(xs...) ⊕ y
.
.
.
end