Julia函数可变参数

Julia可变参数

在一些应用场景中,希望定义的函数能够接收任意数量的参数,例如经典的打印输出函数或字符串格式化函数。如果参数个数固定,在使用这种函数时,只能反复地调用,并需要对结果进行无谓地拼装。

为此,可变参数(Varargs)便成为此时最适合的解决的方案。在Julia中,提供了较为直接、简便的支持,基本定义语法为:

函数名(有序参数表,可变参数名1...; 键值参数表,可变参数名2...)      # 注意此处省略号有实际意义

其中,可变参数名之后会有三点省略号,标识其为可变参数。有序和无序部分都可以提供可变参数,但均只能有一个且需要放在其他常规参数之后,即紧邻界定符之前。

例如,定义如下对任意多个参数进行打印的函数:

julia> function MyShow(x, y, z...)         # x与y是有序参数,z是可变参数
    print(x, " ", y, " ", z)
  end
MyShow (generic function with 1 method)

以不同数量的参数对其调用时,结果如下:
Julia可变参数

可见,可变参变量会以元组结构记录未被参数表“取走”的剩余实参。如果无剩余实参,该可变参变量会成为零元的元组。在函数内部,再采用元组的访问方式解开可变参变量中的元素,例如索引遍历或变量提取等,便于对其实现各处理过程。

另外,可变参数部分也可以进行类型限定,例如:

julia> f(args::Int32...) = args             # 限定可变c参数args为Int32类型
f (generic function with 1 method)

julia> f(Int32(1))
(1, )

julia> f(Int32(1), Int32(2))
(1, 2)

julia> f(Int32(1), Int64(2))                # 可变参数2是Int64类型,所以出错
ERROR: MethodError: no method matching f(::Int32, ::Int64)
Closest candidates are:
  f(::Int32...) at REPL[38]:1

可见,其中的f()函数只支持类型均为Int32类型的任意参数。

基于可变参数机制,我们便可以在调用时提供任意的参数,在内部实现灵活的处理操作。例如,一个支持任意数量的元素进行线性组合的操作函数:

julia> function linear_combine(args...)
          s = 0
          for p in args
            s += p[1]*p[2]
          end
          s
        end
linear_combine (generic function with 1 method)

之后,便可将系数与数值成对提供,实现加权的线性组合:

julia> x = ((4,3+2im), (3.2,3//5));

julia> linear_combine(x...)
13.92 + 8.0im

或者

julia> x = ((2,1.1), (1,3), (4,3+2im), (3.2,3//5));

julia> linear_combine(x...)
19.119999999999997 + 8.0im

当然,存在可变参数的函数在调用时也可以采用可迭代数集展开的方式提供实参。但是因可变参数的存在,不再像数集展开式调用那样有个数的限制,而是可以更多些。例如:
Julia可变参数

此时在所需实参被传递到参变量后,多余的元素会被“余留”在可变参量中,同样会传入到函数内,程序接收后可视需要进行适当的处理。

对于键值部分的可变参数,用法与上面所述的相差不大,但传入到函数内部时,不再是元组,而是命名元组。例如:

julia> varkws(; x=0, kwargs...) = (println(x); kwargs)  # 键值部分只有一个键值对,键名为x
varkws (generic function with 1 method)

调用时,结果类似于:

julia> varkws(x=1, y=2, z=3)                            # y=2, z=3是额外,被处理为可变参数
1
pairs(::NamedTuple) with 2 entries:
  :y => 2
  :z => 3

julia> d = Dict(:x=>8.8, :y=>9.9, :z=>10.10)
Dict{Symbol, Float64} with 3 entries:
  :y => 9.9
  :z => 10.1
  :x => 8.8

julia> varkws(; d...)
8.8                                                        # :x=>8.8 被提取到x参变量中
pairs(::NamedTuple) with 2 entries:
  :y => 9.9
  :z => 10.1

可见,未在参数表列出的键值对会被封装到可变参量中,并被记录在命名元组中;同样支持以字典数集的展开方式提供实参,且会自动传递到同名的参变量中。

如果可变参数需要严格限制个数,则使用Vararg{T, N}这种参数化类型对可变参数进行约束,其中,T用于限制参数类型,N则指定了必需的参数个数。例如:

julia> function varn(x, y, z::Vararg{Any,2})   # 限定额外仍需两个参数
          println(x)
          println(y)
          println(z)
        end
varn (generic function with 1 method)

尝试调用一下:

julia> varn(1, 2, 3, 4)
1
2
(3, 4)

或者使用多元结构展开的方式:

julia> x = (1, 2, 3, 4)
(1, 2, 3, 4)

julia> varn(x...)
1
2
(3, 4)

但如果参数个数不满足要求,会报错,例如:

julia> varn(1, 2)
ERROR: MethodError: no method matching varn(::Int64, ::Int64)

julia> varn(1, 2, 3)
ERROR: MethodError: no method matching varn(::Int64, ::Int64, ::Int64)

julia> varn(1, 2, 3, 4, 5)
ERROR: MethodError: no method matching varn(::Int64, ::Int64, ::Int64, ::Int64, ::Int64)

看起来Vararg的作用有些鸡肋,但若N值是外部某个条件控制的,还是非常有用的。

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!