3

We create a method with splatted arguments and call Method#parameters on it:

def splatter(x, *y, z); end

params = method(:splatter).parameters
  # => [[:req, :x], [:rest, :y], [:req, :z]]

I'm looking for a function f that will map a list of arguments onto their corresponding variable names. The function should be flexible enough to work on any other method with arbitrarily placed splat arguments. For example:

args = [:one, :two, :three, :four]

f(params, args)
  # => [[:x, :one], [:y, :two], [:y, :three], [:z, :four]]

or something along those lines (flipped elements would be fine also). I feel there must be a flexible, elegant solution using inject or something, but I can't seem to come up with it.

4

3 回答 3

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, []]]
于 2013-10-01T07:44:28.900 回答
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
于 2013-10-01T11:44:38.620 回答
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
于 2013-10-01T05:34:08.973 回答