2016-04-14 1 views
4

Je souhaite convertir un proc qui présente un comportement lambda (contrôle d'argument) en un qui ne l'est pas. Ce qui suit est un exemple très artificiel, mais il devrait obtenir le point sur:Ignorer la vérification des arguments lambda

L'objectif est de créer une connexion DSL qui ressemble à ceci:

NumberSeries.perform do 
    add first_series: -> { natural_numbers.take(10) }, 
     second_series: -> { fibonacci_numbers.take(10) } 
end 

Notez que natural_numbers et fibonacci_numbers ne sont pas passés comme arguments dans la DSL. La mise en œuvre de add ressemble à ceci:

NaturalNumbersFibonacciNumbers = Struct.new(:natural_numbers, :fibonacci_numbers) 
FAMOUS_NUMBER_SERIES = NaturalNumbersFibonacciNumbers. 
         new(natural_numbers, fibonacci_numbers) 

def add(first_series:, second_series:) 
    first_numbers = FAMOUS_NUMBER_SERIES.instance_eval(&first_series) 
    second_numbers = FAMOUS_NUMBER_SERIES.instance_eval(&second_series) 
    first_numbers.zip(second_numbers).map { |x, y| x + y } 
end 

Maintenant, si je remplace -> avec proc dans le DSL, cela fonctionnera. Cependant, en gardant les lambdas, je recevrais

ArgumentError: wrong number of arguments (1 for 0)

comme BasicObject#instance_eval cède auto au lambda, mais le lambda attend aucun argument.


Je ne veux pas utiliser Fiddle pour des raisons évidentes.

+2

Est-ce que le travail 'instance_exec' place - qui vous permet de passer des arguments (ou non dans ce cas)? – matt

+0

Je suis confus. Pourquoi ne pas simplement définir votre DSL avec procs? 'add first_series: proc {natural_numbers.take (10)}' est tout aussi élégant. –

+0

@matt, oui, 'instance_exec' a fait l'affaire. Aucune idée de comment je n'ai pas pensé à ça. Même s'il ne répond pas à la question, il résout le problème original, donc ajoutez-le comme réponse et si personne ne sait comment le faire, je l'accepterai. – ndn

Répondre

1

instance_exec est une alternative à instance_eval qui vous permet de contrôler les paramètres transmis au bloc, et ne passe pas le récepteur comme le fait instance_eval. Vous pouvez l'utiliser comme une solution à votre problème car il vous permet de passer de zéro args:

irb:108:0> Object.new.instance_eval &-> { puts "Hello" } 
ArgumentError: wrong number of arguments (given 1, expected 0) 
    from (irb):108:in `block in irb_binding' 
    from (irb):108:in `instance_eval' 
    from (irb):108 
    from /Users/matt/.rubies/ruby-2.3.0/bin/irb:11:in `<main>' 
irb:109:0> Object.new.instance_exec &-> { puts "Hello" } 
Hello 
=> nil