简体   繁体   English

是否可以声明一个以块为默认值的方法?

[英]Is it possible to declare a method with block as default value?

I want to write a method which takes a block and if no block given it should use a default block. 我想编写一个方法,该方法需要一个块,如果没有给出块,则应使用默认块。 So I want to have something like this: 所以我想要这样的东西:

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

But when I'm trying to do so I'm getting the syntax error. 但是,当我尝试这样做时,出现语法错误。

I know I can deal with my problem using block_given? 我知道我可以使用block_given?处理我的问题block_given? . But I am interested in first approach. 但是我对第一种方法感兴趣。 Am I missing something or this is just not possible? 我是否缺少某些东西,或者这根本不可能?

You cannot declare a default block in the method definition, however you can use a little trick to use a custom block if none is given. 您不能在方法定义中声明默认块,但是如果未给出自定义块,则可以使用一些技巧来使用自定义块。

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!

Let me explain it a little bit. 让我解释一下。 Let's start from a more readable version. 让我们从更具可读性的版本开始。

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

You define the method to accept a block, then you check if block is defined. 您定义接受块的方法,然后检查是否定义了块。 If not, you assign a custom block. 如果不是,则分配一个自定义块。 Finally, you execute the block. 最后,执行该块。

Let's enhance it a little bit. 让我们对其进行一些增强。 You can use block_given? 您可以使用block_given吗? to check if a block is passed 检查是否通过了块

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

This also allows you to skip the declaration of the block ( &block ) in the method definition. 这还允许您跳过方法定义中的块声明( &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

But, at this point, you can also use the Proc.new to assign the block to a variable. 但是,此时,您还可以使用Proc.new将块分配给变量。

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

As a final word, I'm trying to understand when this approach would make sense. 最后,我试图了解这种方法何时可行。 In most cases, you can probably wrap the code in a class or module and pass it as argument. 在大多数情况下,您可以将代码包装在类或模块中,并将其作为参数传递。 It's probably better. 可能更好。

You can do it with regular lambdas. 您可以使用常规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?

Not sure if you can do this with blocks, though. 不过,不确定是否可以使用块执行此操作。 A block is not an ordinary parameter. 块不是普通参数。

No, you can't provide a default block value in a method definition. 不可以,您不能在方法定义中提供默认的块值。 You can, however, achieve the equivalent behavior through the use of block_given? 但是,您可以通过使用block_given?实现相同的行为block_given? within the body of the method, as follows: 在方法主体内,如下所示:

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

However, in this scenario you can't utilize yield to invoke any block that is passed in, since it won't be there in the default case. 但是,在这种情况下,您不能利用yield来调用传入的任何块,因为在默认情况下它将不存在。 You'll have to invoke the block Proc object, as in block.(name) . 您必须像block.(name)一样调用block Proc对象。

Some answers suggest using block_given? 一些答案建议使用block_given? , but since there is no possibility that a block would be nil or false when it is given, you can simply use ||= . ,但是由于块在给出时不可能为nilfalse ,因此您可以简单地使用||=

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM