【问题标题】:Is it possible to declare a method with block as default value?是否可以声明一个带有块作为默认值的方法?
【发布时间】:2013-12-01 16:35:51
【问题描述】:

我想编写一个接受块的方法,如果没有给定块,它应该使用默认块。所以我想要这样的东西:

def say_hello(name, &block = ->(name) { puts "Hi, #{name}" })
  # do something
end 

但是当我尝试这样做时,我遇到了语法错误。

我知道我可以使用block_given? 来解决我的问题。但我对第一种方法感兴趣。 是我遗漏了什么还是这是不可能的?

【问题讨论】:

    标签: ruby lambda block


    【解决方案1】:

    一些答案​​建议使用block_given?,但由于给出的块不可能是nilfalse,您可以简单地使用||=

    def say_hello(name, &block)
      block ||= ->(name){puts "Hi, #{name}"}
      # do something
    end
    

    【讨论】:

    • 你不是也必须切换访问块的方法以避免yield吗?
    • @PeterAlfvin 我不明白你的意思。
    • 如果调用中没有提供块,那么yield 语句将不起作用。您已经使用 block.call(...) 或类似的东西调用它。
    • @PeterAlfvin 没错。而且,yield 有什么关系?你想告诉我什么?
    • 我想告诉你,这种在调用者不提供值的情况下为block 分配值的技术不是为&block 提供默认值的透明替代方案(即提供一个“默认块”以防调用者不提供)。 OP 需要知道,如果他以前使用过yield,他需要更改他的代码(即# do something 的内容)以使用这种替代方法。
    【解决方案2】:

    您不能在方法定义中声明默认块,但是如果没有给出自定义块,您可以使用一个小技巧来使用自定义块。

    def say_hello(name)
      block = block_given? ? Proc.new : ->(name) { puts "Hi, #{name}" }
      block.call(name)
    end
    
    # This example uses a custom block
    say_hello('weppos') { |name| puts "Hello, #{name}!" }
    # => Hello, weppos!
    
    # This example fallbacks to the default
    say_hello('weppos')
    # => Hi, weppos!
    

    让我稍微解释一下。让我们从更易读的版本开始。

    def say_hello(name, &block)
      block = block ? block : ->(name) { puts "Hi, #{name}" }
      block.call(name)
    end
    

    您定义接受块的方法,然后检查是否定义了块。如果没有,您分配一个自定义块。最后,执行块。

    让我们稍微增强一下。你可以使用block_given吗?检查一个块是否通过

    def say_hello(name, &block)
      block = block_given? ? block : ->(name) { puts "Hi, #{name}" }
      block.call(name)
    end
    

    这也允许您在方法定义中跳过块 (&block) 的声明。

    def say_hello(name)
      if block_given?
        yield name
      else
        # This is rendundant, but it's for clarity
        block = ->(name) { puts "Hi, #{name}" }
        block.call(name)
      end
    end
    

    但是,此时,您也可以使用Proc.new 将块分配给变量。

    def say_hello(name)
      block = block_given? ? Proc.new : ->(name) { puts "Hi, #{name}" }
      block.call(name)
    end
    

    最后,我试图了解这种方法何时有意义。在大多数情况下,您可以将代码包装在类或模块中并将其作为参数传递。这可能会更好。

    【讨论】:

    • 我发布我的答案时没有看到您的答案。如果您使用&block 作为参数,如果没有传递一个块,您不必将block 定义为Proc.new,因为Ruby 会为您处理这些。对吗?
    • 我必须写下我知道block_given? 做什么。 :) 虽然很棒的答案。我目前正在玩一些需要在method_missing 上声明方法的东西。例如foobar { |x| puts x } 将向当前对象类添加一个方法foobar(x)
    【解决方案3】:

    您可以使用常规 lambda 来做到这一点。

    def say_hello(name, block = ->(name) { puts "Hi, #{name}" })
      block.call(name)
    end 
    
    say_hello("Sergio")
    say_hello("Ivan", ->(name) { puts "Where are you from, #{name}?"})
    # >> Hi, Sergio
    # >> Where are you from, Ivan?
    

    不过,不确定您是否可以使用积木来做到这一点。块不是普通的参数。

    【讨论】:

      【解决方案4】:

      不,您不能在方法定义中提供默认块值。但是,您可以通过在方法主体中使用 block_given? 来实现等效行为,如下所示:

      def say_hello(name, &block)
        block = ->(name) { puts "Hi, #{name}" } unless block_given?
        # do something
      end
      

      但是,在这种情况下,您不能使用yield 来调用传入的任何块,因为在默认情况下它不会存在。您必须调用block Proc 对象,如block.(name)

      【讨论】:

        猜你喜欢
        • 2021-10-29
        • 2011-08-19
        • 1970-01-01
        • 2013-07-05
        • 2021-11-02
        • 1970-01-01
        • 1970-01-01
        • 2012-02-24
        • 1970-01-01
        相关资源
        最近更新 更多