问题
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