2013-10-01 3 views
3

Мы создаем метод с 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 или что-то в этом роде, но я не могу придумать его.

+1

Интересный вопрос. Для произвольных методов может быть что-то, что вы можете сделать с информацией, которую вы можете получить из объекта последовательности команд ('RubyVM :: InstructionSequence.of (method (: splatter))). Если вы вызываете 'to_a', 12-й элемент содержит индексы различных конфигураций аргументов. К сожалению, я слишком устал, чтобы думать об этом дальше этого момента, но, надеюсь, это может быть полезно. –

+0

Ницца, я не знал, что вы могли бы это сделать ... Мне нужно будет углубиться в InstructionSequence, чтобы узнать, какую информацию я могу получить от него. Благодаря! – rickyrickyrice

+1

Если вы ищете тестовые примеры, вы можете найти [этот мой ответ] (http://stackoverflow.com/a/17173107/2988) полезным. –

ответ

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, []]] 
+0

Я случайно отклонил ваш вопрос, но не могу удалить свой голос, если вы не отредактируете свой ответ. Простите за это! –

+0

Это, безусловно, шаг в правильном направлении, я думаю. Это не дает вполне ответ, который я хочу, но его было бы нелегко настроить. Я собираюсь подождать еще немного, чтобы узнать, может ли кто-нибудь придумать что-нибудь еще. Назовите это догадкой, но я действительно чувствую, что есть более простое решение. – rickyrickyrice

+0

@ rickyrickyrice его можно изменить в соответствии с вашим форматом. Не был уверен, что должно выглядеть так, если аргумент splat является 'blank' или' [] '? – tihom

0

Edit: После моего первоначального замешательства:

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 
+0

Спасибо за ответ! Да, я бы предпочел что-то, что работает для произвольно размещенных символов/требуемых параметров. Я уточню вопрос. – rickyrickyrice

+0

Сундуот был заработал! Я как-то упустил тип arg, хотя раньше видел этот метод. После завтрака я сделаю редактирование. –

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 

Еще один пример с двумя аргументами, знак первой:

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 
+0

Очень интересный подход! Определенно давая мне некоторое представление о проблемной области. Это, вероятно, будет звучать раздражающе вручную, но я действительно ищу что-то элегантное, которое включает в себя функциональные операции, такие как складка, карта или почтовый какой-то тип. Но спасибо за предложение! – rickyrickyrice

+0

Хороший подход +1, имеет потенциал быть очень общим (включая входные блоки, параметры параметров и т. Д.), Есть старый плагин вдоль одной строки: [merb-action-args] (https://github.com/merb/ Merb/дерево/ведущий/Merb-действие-арг) – tihom

Смежные вопросы