让我们假设我想编写一个接受任何关联运算符⊕的函数,并向其中添加方法,以便我可以用函数替换任何值。这些其他方法的语义如下:
f
和g
,则结果应为首先将f
和g
(独立)应用于其运算符的函数参数,然后将applies应用于结果。f
,但另一个参数是任何非函数值x
,则结果是一个函数,该函数首先将f
应用于其参数,然后将applies应用于结果和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
但是,当我尝试编译此函数时,出现以下错误:
ERROR: syntax: cannot add method to function argument ⊕
我知道我可以编写一个高阶函数,该函数返回基于给定函数的 new 函数/运算符。例如:
associative(⊞) = begin
let ⊕(x) = x
⊕(x, y) = x ⊞ y
⊕(x, y, zs...) = ⊕(x⊞y, zs...)
associative!(⊕)
⊕
end
end
如果我在这里内联associative!
的定义,那么associative
可以正常工作,我可以写:
⊕ = associative(+)
⊗ = associative(*)
f(x) = 3⊗x ⊕ 1
f(1) # 4
f(cos)(0) # 4
但是我认为最好有一个变异版本。我认为将associate!
重写为宏是可以的,但实际上似乎没有什么需要在这里使用宏。因此,是否可以将其作为高阶函数来执行?如果可以,怎么做?
答案 0 :(得分:1)
我不知道如何书写您的符号,但是这是否会使您对想要的东西有所了解?
=QUERY(A:B, "select * where A = 'Customer'")
答案 1 :(得分:0)
这里发生了一些事情。首先要注意的是⊕(<args...>) = <body...>
的第一个实例将创建一个新函数并将其绑定到⊕
。但是,它显然不会为⊕
创建新的绑定。我猜这就是为什么错误消息中提到⊕
是一个函数参数的原因,但是无论如何这都是一个红色的提示-它只会创建一个新函数,而不是对现有函数进行突变。
在this thread的结尾处,我们看到了first hint at a solution。为了动态地向函数别名添加方法,我们基本上需要使用与为任意数据类型定义可调用对象相同的语法:
(::typeof(⊕))(<args...>) = <body...>
通过此更改,我们得到了另一个错误:
Global method definition around ... needs to be placed at the top level, or use "eval"
在同一线程中再进一步一点,我们看到这是由于Julia 0.6之后的某个时间所引起的,因此我们now need to @eval
这些表达式。现在我们涉及@eval
,我们需要牢记以下几点:
⊕
拼接对$⊕
的引用$
前面的⊕
似乎干扰了Julia并将其识别为中缀运算符。因此,我们需要改用功能(前缀)表示法。将其放在一起,最终得到如下结果:
@eval (::typeof($⊕))(f::F, y) where F<:Function = (xs...) -> $⊕(f(xs...), y)
如果我们真的想使用中缀符号,可以在let
内使用@eval
绑定:
@eval let ⊕ = $⊕
(::typeof($⊕))(f::F, y) where F<:Function = (xs...) -> f(xs...) ⊕ y
.
.
.
end