如何编写将任意数量的参数传递给另一个函数的Julia函数?

如何编写将任意数量的参数传递给另一个函数的Julia函数?,julia,variadic-functions,Julia,Variadic Functions,我是Julia的新手,在R方面更有经验,所以对于那些熟悉这两个方面的人来说,这里有一段R代码,我正试图在Julia中复制它 f = function(a, b = 1, c = 2) a + 2*b = 3*c g = function(d, ...) 5 + f(d, ...) 这样调用g(1)是有效的,它将使用f的默认值进行计算,或者您可以指定g(1,b=3)或g(1,c=4,b=2)或其他任何内容。当然,关键是只要指定了名称,就可以将任意可选参数的排列传递给f,而无需考虑顺序 在《朱莉娅

我是Julia的新手,在R方面更有经验,所以对于那些熟悉这两个方面的人来说,这里有一段R代码,我正试图在Julia中复制它

f = function(a, b = 1, c = 2) a + 2*b = 3*c
g = function(d, ...) 5 + f(d, ...)
这样调用
g(1)
是有效的,它将使用f的默认值进行计算,或者您可以指定
g(1,b=3)
g(1,c=4,b=2)
或其他任何内容。当然,关键是只要指定了名称,就可以将任意可选参数的排列传递给f,而无需考虑顺序

在《朱莉娅》中,我在这方面遇到了一些麻烦,因为varargs的工作方式有点不同。我知道您可以以元组的形式向函数传递无限参数列表(例如,
function g(d::Number,f_args::NamedTuple
),但它似乎不适用于可选的关键字参数。这几乎就像是
f_args
元组在某个地方丢失了名称,而
f
由于看到3个没有名称的参数而不知道选择哪种方法而变得混乱

如果真的有可能的话,如果有人能告诉我如何在Julia中做到这一点,我将非常感激。我希望保留这些论点的可选性,以及它们的名称。我还希望能够保留它们的类型(也就是说,我希望
f
的每个参数只有在符合正确类型的情况下才可以接受,这当然通常是在Julia中使用
操作符在标题中完成的)


PS-就我的问题而言,重写
g
使其具有
c
b
作为可选参数对我来说是不够的。为了简单起见,我展示了
f
g
的示例,但实际上我希望将这些原则应用于具有许多参数的更复杂函数。

,可选位置参数和关键字参数的处理方式不同

如果我们想在函数中允许任意参数顺序,我们必须指定。与R不同,julia坚持在参数列表中使用分号来区分位置参数和关键字参数。我们可以在julia中编写函数
f
g
,只需添加适当的分号即可s:

f(a; b=1, c=2) = a + 2*b + 3*c
g(d; kwargs...) = 5 + f(d; kwargs...)
这定义了一个带有一个必需位置参数和两个可选关键字参数的方法
f
,以及另一个带有一个必需位置参数的方法
g
,该方法将传递给
f
。这意味着我们可以执行以下操作:

g(1)             # 14
g(1) == 5 + f(1) # true
g(1, b=3)        # 18
g(1, c=4, b=2)   # 22
同样,与R中不同,关键字参数与位置参数不同,因此我们不能这样做:

f(1, 2, 4)
# ERROR: MethodError: no method matching f(::Int64, ::Int64, ::Int64)
# Closest candidates are:
#   f(::Any; b, c) at REPL[1]:1
这是因为我们定义的方法签名只接受一个位置参数。如果您想要一个可以接受位置参数或关键字参数的函数,我们必须定义一个新的函数签名,如下所示:

f(a, x=1, y=2) = f(a; b=x, c=y)
g(1, 2) == g(1, b=2)         # true
g(1, 2, 4) == g(1, c=4, b=2) # true
在这里,我们正在创建一些与原始方法同名的新方法,它们所做的只是将其位置参数作为关键字参数传递。现在我们可以这样做:

f(1, 2, 4) == 17  # true
如果我们希望
g
以类似的方式运行,请记住julia区分了位置参数和关键字参数,我们需要指定两个变量:一个用于位置参数,一个用于关键字参数:

g(d, args...; kwargs...) = 5 + f(d, args...; kwargs...)
现在我们可以这样做:

f(a, x=1, y=2) = f(a; b=x, c=y)
g(1, 2) == g(1, b=2)         # true
g(1, 2, 4) == g(1, c=4, b=2) # true
但是,当我们尝试将这些类型的签名组合在一起时,事情会变得一团糟:

g(1, 2, c=4)
# ERROR: MethodError: no method matching f(::Int64, ::Int64; c=4)
# Closest candidates are:
#  f(::Any, ::Any) at REPL[18]:1 got unsupported keyword argument "c"
#  f(::Any, ::Any, ::Any) at REPL[18]:1 got unsupported keyword argument "c"
#  f(::Any; b, c) at REPL[18]:1
为什么不允许这样做?错误消息给我们一个提示,说明定义在引擎盖下要做什么。当我们定义一个具有n个可选位置参数的方法时,julia只生成n+1个方法,每个方法的签名末尾都标记有0到n个参数。如下所示:

h(a, b=1, c=2, d=3) = nothing
# h (generic function with 4 methods)

methods(h)
# 4 methods for generic function "h":
#[1] h(a) in Main at REPL[32]:1
#[2] h(a, b) in Main at REPL[32]:1
#[3] h(a, b, c) in Main at REPL[32]:1
#[4] h(a, b, c, d) in Main at REPL[32]:1

这意味着当我们定义了可选的位置参数版本的
f
时,julia为我们制作了签名
f(a,b)
f(a,b,c)
,但它没有制作带有签名的方法
f(a,b;c)

非常感谢您的透彻解释!我在
g
的标题中遗漏了分号。非常感谢您抽出时间来做这件事!