我们使用splatted参数创建一个方法,并在其上调用Method#parameters
:
def splatter(x, *y, z); end
params = method(:splatter).parameters
# => [[:req, :x], [:rest, :y], [:req, :z]]
我正在寻找一个函数f
,它将一个参数列表映射到它们对应的变量名。 该函数应该足够灵活,可以使用任意放置的splat参数来处理任何其他方法。例如:
args = [:one, :two, :three, :four]
f(params, args)
# => [[:x, :one], [:y, :two], [:y, :three], [:z, :four]]
或沿着那些线的东西(翻转元素也可以)。我觉得必须有一个灵活,优雅的解决方案,使用inject
或其他东西,但我似乎无法想出它。
答案 0 :(得分:3)
def f(params,*args)
# elements to be assigned to splat parameter
splat = args.count - params.count + 1
# will throw an error if splat < 0 as that means not enough inputs given
params.map{ |p|
[ p[1] , ( p.first == :rest ? args.shift(splat) : args.shift ) ]
}
end
实施例
def splatter(x,*y,z)
# some code
end
f(method(:splatter).parameters, 1,2,3,4)
#=>[[:x, 1], [:y, [2, 3]], [:z, 4]]
def splatter(x,y,*z)
# some code
end
f(method(:splatter).parameters, 1,2,3,4)
# => [[:x, 1], [:y, 2], [:z, [3, 4]]]
def splatter(x,*z)
# some code
end
f(method(:splatter).parameters, 1)
# => [[:x, 1], [:z, []]]
答案 1 :(得分:2)
我认为这是eval
有用的好例子。下面的代码生成一个lambda,它接受指定的相同参数并吐出已解析的参数列表。这种方法的优点是使用了Ruby自己的解析splats的算法。
def resolve(parameters,args)
param_list = parameters.map do |type,name|
splat = '*' if type == :rest
"#{splat}#{name}"
end.join(',')
source = ""
source << "->(#{param_list}) do\n"
source << " res = []\n"
parameters.each do |type,name|
if type == :rest
source << " res += #{name}.map {|v| [:'#{name}',v] }\n"
else
source << " res << [:'#{name}',#{name}]\n"
end
end
source << "end"
eval(source).call(*args)
end
示例:
params = ->(x,*y,z){}.parameters
resolve(params,[:one, :two, :three, :four])
#=> [[:x, :one], [:y, :two], [:y, :three], [:z, :four]]
在幕后,生成了以下代码:
->(x,*y,z) do
res = []
res << [:'x',x]
res += y.map {|v| [:'y',v] }
res << [:'z',z]
end
另一个带有两个参数的例子,首先是splat:
params = ->(*x,y){}.parameters
resolve(params,[:one, :two, :three, :four])
#=> [[:x, :one], [:x, :two], [:x, :three], [:y, :four]]
生成的代码为
->(*x,y) do
res = []
res += x.map {|v| [:'x',v] }
res << [:'y',y]
end
答案 2 :(得分:0)
编辑:在我最初的困惑之后:
def doit(params, args)
rest_ndx = params.map(&:first).index(:rest)
to_insert = [params[rest_ndx].last]*(args.size-params.size) if rest_ndx
params = params.map(&:last)
params.insert(rest_ndx,*to_insert) if rest_ndx
params.zip(args)
end