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

无人久伴 提交于 2020-08-07 07:48:46

问题


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?. But I am interested in first approach. Am I missing something or this is just not possible?


回答1:


Some answers suggest using block_given?, but since there is no possibility that a block would be nil or false when it is given, you can simply use ||=.

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



回答2:


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? 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.

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.

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.




回答3:


You can do it with regular lambdas.

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.




回答4:


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? 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. You'll have to invoke the block Proc object, as in block.(name).



来源:https://stackoverflow.com/questions/20314478/is-it-possible-to-declare-a-method-with-block-as-default-value

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!