Can I enforce arity on a block passed to a method?
有没有办法"打开"使用
我的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Command attr_reader :name, :action def initialize(name, &action) @name = name @action = action end def perform(*args) begin action.call(*args) rescue ArgumentError raise(WrongArity.new(args.size)) end end end class WrongArity < StandardError; end |
不幸的是,默认情况下,
1 2 | c = Command.new('second_argument') { |_, y| y } c.perform(1) # => nil |
号
还有其他想法吗?或者更好的解决问题的方法?
谢谢!
您的
1 2 3 4 5 6 | def perform(*args) if args.size != @action.arity raise WrongArity.new(args.size) end @action.call(*args) end |
这应该考虑到像
1 2 3 4 5 6 7 8 | def perform(*args) if @action.arity >= 0 && args.size != @action.arity raise WrongArity.new(args.size) elsif @action.arity < 0 && args.size < -(@action.arity + 1) raise WrongArity.new(args.size) end @action.call(*args) end |
号
根据这个答案,将proc转换为lambda的唯一方法是使用
define_method always defines a method without the tricks [i.e. a lambda-style Proc], even if a non-lambda Proc object is given. This is the only exception for which the tricks are not preserved.
号
具体来说,在实际定义方法的同时,
所以你可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def initialize(name, &action) @name = name @action = to_lambda(&action) end def perform(*args) action.call(*args) # Could rescue ArgumentError and re-raise a WrongArity, but why bother? # The default is"ArgumentError: wrong number of arguments (0 for 1)", # doesn't that say it all? end private def to_lambda(&proc) Object.new.define_singleton_method(:_, &proc) end |
。
您的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Command attr_reader :name, :action def initialize(name) # The block argument is removed @name = name @action = lambda # We replace `action` with just `lambda` end def perform(*args) begin action.call(*args) rescue ArgumentError raise(WrongArity.new(args.size)) end end end class WrongArity < StandardError; end |
一些参考文献:如果从方法内部调用proc.new而不带任何参数,它将返回一个新的proc,其中包含为其周围方法指定的块。--http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html
结果证明lambda的工作方式是相同的。