Inconsistency of arity between Hash.each and lambdas

前端 未结 2 1669
死守一世寂寞
死守一世寂寞 2021-01-06 16:59

I lifted the following example from Josh Susser

  def strip_accents params
    thunk = lambda do |key,value|
      case value
        when String then value.         


        
相关标签:
2条回答
  • 2021-01-06 17:16

    Indeed, it appears to have changed between 1.8 and 1.9, but this change fixes it for 1.9.2, at least in my tests:

    def strip_accents params
      thunk = lambda do |h|
        key, value = h
        case value
        when String then value.remove_accents!
        when Hash   then value.each(&thunk)
        end
      end
      params.each(&thunk)
    end
    

    This approach turns out to be backward-compatible with Ruby 1.8.7, as well.

    0 讨论(0)
  • 2021-01-06 17:16

    Hash#each, just like every other #each method, yields one argument to the block. In the case of Hash#each, that one argument is a two-element array consisting of the key and the value.

    So, Hash#each yields one argument, but your lambda has two mandatory parameters, therefore you get an arity error.

    It works with blocks, since blocks are less strict about their arguments, and in particular, if a block has multiple parameters, but only gets one argument, it will try to deconstruct the argument as if it had been passed in with a splat.

    There are two kinds of Procs: lambdas and non-lambdas (confusingly, the latter are usually also called Procs). Lambdas behave like methods in terms of how the return keyword behaves and (more importantly, for this case) how they bind arguments, whereas non-lambda Procs behave like blocks in terms of how return and argument binding work. That's why Proc.new (which creates a non-lambda Proc) works, but lambda (which obviously creates a lambda) doesn't.

    You can check whether a Proc is a lambda or not by calling Proc#lambda?.

    If you want to deconstruct the argument, you will have to do so explicitly, the same way you would when you define a method:

    lambda do |(key, value)|
    

    And, yes, a more sane approach to argument binding for blocks, Procs and lambdas was one of the major backwards-incompatible changes in Ruby 1.9.

    0 讨论(0)
提交回复
热议问题