3

分割された引数を持つメソッドを作成し、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ないようです。

4

3 に答える 3

2

evalこれは役に立つ良い例だと思います。以下のコードは、指定されたものと同じ引数を取り、解決された引数のリストを吐き出すラムダを生成します。このアプローチの利点は、スプラットを解決するための Ruby 独自のアルゴリズムが使用されることです。

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

2 つの引数を持つ別の例、最初にスプラ:

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 に答える