def say_hi( name )
puts "Hi, #{name}!"
if block_given?
yield( name )
end
end
say_hi("Mridang") do |str|
puts "Your name has #{str.length} letters."
end
#=> Hi, Mridang!
#=> Your name has 7 letters.
def say_hi( name )
puts "Hi, #{name}!"
yield( name, name.reverse ) if block_given?
end
say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" }
#=> Hi, Mridang!
#=> Is your name Mridang or gnadirM?
I offer up my own explanation from this answer, slightly modified:
"Blocks" in Ruby are not the same as the general programming terms "code block" or "block of code".
Pretend for a moment that the following (invalid) Ruby code actually worked:
def add10( n )
puts "#{n} + 10 = #{n+10}"
end
def do_something_with_digits( method )
1.upto(9) do |i|
method(i)
end
end
do_something_with_digits( add10 )
#=> "1 + 10 = 11"
#=> "2 + 10 = 12"
...
#=> "9 + 10 = 19"
While that code is invalid, its intent—passing some code to a method and having that method run the code—is possible in Ruby in a variety of ways. One of those ways is "Blocks".
A Block in Ruby is very, very much like a method: it can take some arguments and run code for those. Whenever you see foo{ |x,y,z| ... } or foo do |x,y,z| ... end, those are blocks that take three parameters and run the ... on them. (You might even see that the upto method above is being passed a block.)
Because Blocks are a special part of the Ruby syntax, every method is allowed to be passed a block. Whether or not the method uses the block is up to the method. For example:
def say_hi( name )
puts "Hi, #{name}!"
end
say_hi("Mom") do
puts "YOU SUCK!"
end
#=> Hi, Mom!
The method above is passed a block that is ready to issue an insult, but since the method never calls the block, only the nice message is printed. Here's how we call the block from a method:
def say_hi( name )
puts "Hi, #{name}!"
if block_given?
yield( name )
end
end
say_hi("Mridang") do |str|
puts "Your name has #{str.length} letters."
end
#=> Hi, Mridang!
#=> Your name has 7 letters.
We use block_given? to see whether or not a block was passed along or not. In this case we passed an argument back to the block; it's up to your method to decide what to pass to the block. For example:
def say_hi( name )
puts "Hi, #{name}!"
yield( name, name.reverse ) if block_given?
end
say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" }
#=> Hi, Mridang!
#=> Is your name Mridang or gnadirM?
It's just a convention (and a good one, and one you want to support) for some classes to pass the instance just created to the block.
This is not an exhaustive answer, as it does not cover capturing blocks as arguments, how they handle arity, un-splatting in block parameters, etc. but intends to serve as a Blocks-Are-Lambdas intro.
Ruby blocks are a way of creating Proc objects which represent code that can be used by other code. Proc objects are instructions between curly braces {} (or do...end phrases for multiline blocks, which have lower precedence than curly braces) which may optionally take arguments and return values (e.g. {|x,y| x+y}). Procs are first-class objects and can be constructed explicitly or attained implicitly as method pseudo-arguments:
Construction as a Proc object (or using the lambda keyword):
add1 = Proc.new {|x| x+1} # Returns its argument plus one.
add1.call(1) # => 2
Passed as a method pseudo argument, either explicitly using the special & last-argument syntax sugar operator or implicitly using a block_given?/yield pair:
def twice_do(&proc) # "proc" is the block given to a call of this method.
2.times { proc.call() } if proc
end
twice_do { puts "OK" } # Prints "OK" twice on separate lines.
def thrice_do() # if a block is given it can be called with "yield".
3.times { yield } if block_given?
end
thrice_do { puts "OK" } # Prints "OK" thrice on separate lines.
The second form is typically used for Visitor patterns; data can be passed to the special block arguments as arguments to the call or yield methods.
2.times { print "Yes, I've used chunky bacon in my examples, but never again!" } is an example.
With blocks, you can group a set of instructions together so that they can be passed around your program. The curly braces give the appearance of crab pincers that have snatched the code and are holding it together. When you see these two pincers, remember that the code inside has been pressed into a single unit. It’s like one of those little Hello Kitty boxes they sell at the mall that’s stuffed with tiny pencils and microscopic paper, all crammed into a glittery transparent case that can be concealed in your palm for covert stationary operations. Except that blocks don’t require so much squinting. The curly braces can also be traded for the words do and end, which is nice if your block is longer than one line.
loop do
print "Much better."
print "Ah. More space!"
print "My back was killin' me in those crab pincers."
end
Block arguments are a set of variables surrounded by pipe characters and separated by commas.
|x|, |x,y|, and |up, down, all_around| are examples.
Block arguments are used at the beginning of a block.
{ |x,y| x + y }
In the above example, |x,y| are the arguments. After the arguments, we have a bit of code. The expression x + y adds the two arguments together. I like to think of the pipe characters as representing a tunnel. They give the appearance of a chute that the variables are sliding down. (An x goes down spread eagle, while the y neatly crosses her legs.) This chute acts as a passageway between blocks and the world around them. Variables are passed through this chute (or tunnel) into the block.
delegate(string name)
{
Console.WriteLine("Hello there {0}", name);
}
在 Ruby 和 C# 中,都允许使用多个语句。在 Ruby 中,需要使用上面的第二种语法。
这些概念在许多其他受到函数式编程背后的思想影响的语言中也可用。
For anybody coming to this question from a C# background (or other langs really), this might help:
Ruby blocks are like lambda expressions and anonymous methods in C#. They are what C# calls delegates (and Ruby calls Procs), which is to say that they are essentially functions that can be passed as values. In both Ruby and C#, they can also behave as closures.
Ruby: { |x| x + 1 }
C#: x => x + 1
Ruby: { |name| puts "Hello there #{name}" }
C#: name => { Console.WriteLine("Hello there {0}", name); }
Both C# and Ruby offer alternative ways to write the above example.
Ruby:
do |name|
puts "Hello there #{name}"
end
C#:
delegate(string name)
{
Console.WriteLine("Hello there {0}", name);
}
In both Ruby and C#, multiple statements are allowed, In Ruby, the second syntax above is required for this.
These concepts are available in many other languages that have been influenced by the ideas behind functional programming.
In 1.9+, the parameter list passed into a block became more sophisticated, allowing local variables to be defined:
do |a,b;c,d|
some_stuff
end
;c,d declare two new local variables inside the block, that do not receive values from the called-routine's yield statement. Ruby 1.9+ guarantees that, if the variables existed outside the block, that they will not be stomped on by the same-named variables inside the block. This is new behavior; 1.8 would stomp on them.
def blah
yield 1,2,3,4
end
c = 'foo'
d = 'bar'
blah { |a, *b; c,d|
c = 'hello'
d = 'world'
puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}", "d: #{d}"
}
puts c, d
# >> a: 1
# >> b: 2,3,4
# >> c: hello
# >> d: world
# >> foo
# >> bar
There's also the "splat" operator *, which works in the list of parameters:
do |a,*b|
some_stuff
end
Would assign the first of multiple values to "a", and all the rest would be captured in "b" which would be treated like an array. The * could be on the a variable:
do |*a,b|
some_stuff
end
would capture all passed in variables except the last one, which would be passed to b. And, similarly to the two previous:
do |a,*b,c|
some_stuff
end
would assign the first value to a, the last value to c and all/any intervening values to b.
Blocks are lightweight literals for anonymous first-class procedures with some annoying limitations. They work the same way in Ruby as they work in pretty much every other programming language, modulo the afore-mentioned limitations, which are:
blocks can only appear in argument lists
at most one block can appear in an argument list (and it must be the last argument)
array = [1,2]
puts array.map{ |n| n*10 } # puts (array.map{ |n| n*10 })
=> 10
=> 20
puts array.map do |n| n*10 end # (puts array.map) do |n| n*10 end
=> <Enumerator:0x00000100862670>
Blocks are a way of grouping code in Ruby. There are two ways to write blocks. One is using the do..end statement and the other is surrounding the code in curly braces: {}. Blocks are considered objects in the Ruby programming language, and by default all functions accept an implicit block argument.
Here are two examples of blocks that do the same thing:
2.times { puts 'hi' }
2.times do
puts 'hi'
end
Blocks can receive lists of comma-separated arguments inside vertical bars ||. For example:
[1,2].map{ |n| n+2 } # [3, 4]
Blocks (in ruby 1.9.2) can explicitly have local variables:
x = 'hello'
2.times do |;x|
x = 'world'
puts x
end
=> world
=> world
Local variables can be combined with parameters:
[1,2].map{ |n;x| n+2 }
All functions can receive a default block argument:
What is the difference between do..end and {} blocks? By convention {} blocks are on a single line and do..end blocks span multiple lines, since they are each easier to read this way. The main difference has to do with precedence though:
array = [1,2]
puts array.map{ |n| n*10 } # puts (array.map{ |n| n*10 })
=> 10
=> 20
puts array.map do |n| n*10 end # (puts array.map) do |n| n*10 end
=> <Enumerator:0x00000100862670>
发布评论
评论(7)
我提供了我自己的解释 这个答案,稍作修改:
Ruby 中的“块”与一般编程术语“代码块”或“代码块”不同。
假设以下(无效)Ruby 代码确实有效:
虽然该代码无效,但其意图(将一些代码传递给方法并让该方法运行该代码)在 Ruby 中可以通过多种方式实现。其中一种方法是“块”。
Ruby 中的块非常非常像方法:它可以接受一些参数并为这些参数运行代码。每当你看到
foo{ |x,y,z| ... }
或foo do |x,y,z| ... end
,这些块采用三个参数并对它们运行...
。 (您甚至可能会看到上面的upto
方法正在传递一个块。)因为块是 Ruby 语法的特殊部分,所以允许每个方法传递一个堵塞。方法是否使用块取决于方法。例如:
上面的方法传递了一个准备发出侮辱的块,但由于该方法从不调用该块,因此只打印好消息。下面是我们如何从方法中调用块:
我们使用
block_given?
来查看块是否被传递。在本例中,我们将一个参数传递回块;由您的方法来决定将什么传递给块。例如:这只是某些类将刚刚创建的实例传递给块的一种约定(也是一个很好的约定,也是您想要支持的约定)。
这不是一个详尽的答案,因为它没有涵盖捕获块作为参数、它们如何处理数量、块参数中的不扩散等,但旨在作为 Blocks-Are-Lambdas 介绍。 >
I offer up my own explanation from this answer, slightly modified:
"Blocks" in Ruby are not the same as the general programming terms "code block" or "block of code".
Pretend for a moment that the following (invalid) Ruby code actually worked:
While that code is invalid, its intent—passing some code to a method and having that method run the code—is possible in Ruby in a variety of ways. One of those ways is "Blocks".
A Block in Ruby is very, very much like a method: it can take some arguments and run code for those. Whenever you see
foo{ |x,y,z| ... }
orfoo do |x,y,z| ... end
, those are blocks that take three parameters and run the...
on them. (You might even see that theupto
method above is being passed a block.)Because Blocks are a special part of the Ruby syntax, every method is allowed to be passed a block. Whether or not the method uses the block is up to the method. For example:
The method above is passed a block that is ready to issue an insult, but since the method never calls the block, only the nice message is printed. Here's how we call the block from a method:
We use
block_given?
to see whether or not a block was passed along or not. In this case we passed an argument back to the block; it's up to your method to decide what to pass to the block. For example:It's just a convention (and a good one, and one you want to support) for some classes to pass the instance just created to the block.
This is not an exhaustive answer, as it does not cover capturing blocks as arguments, how they handle arity, un-splatting in block parameters, etc. but intends to serve as a Blocks-Are-Lambdas intro.
Ruby 块是创建
Proc
对象 代表可以被其他代码使用的代码。 Proc 对象是大括号{}
之间的指令(或多行块的do...end
短语,其优先级低于大括号),可以选择接受参数并返回值(例如{|x,y| x+y}
)。 Proc 是第一类对象,可以显式构造或作为方法伪隐式获得参数:构造为 Proc 对象(或使用
lambda
关键字):作为方法伪参数传递,显式使用特殊的
&
最后参数语法糖运算符或隐式使用block_given?
/产量
对:第二种形式通常用于访问者模式;数据可以作为
call
或yield
方法的参数传递给特殊块参数。Ruby blocks are a way of creating
Proc
objects which represent code that can be used by other code. Proc objects are instructions between curly braces{}
(ordo...end
phrases for multiline blocks, which have lower precedence than curly braces) which may optionally take arguments and return values (e.g.{|x,y| x+y}
). Procs are first-class objects and can be constructed explicitly or attained implicitly as method pseudo-arguments:Construction as a Proc object (or using the
lambda
keyword):Passed as a method pseudo argument, either explicitly using the special
&
last-argument syntax sugar operator or implicitly using ablock_given?
/yield
pair:The second form is typically used for Visitor patterns; data can be passed to the special block arguments as arguments to the
call
oryield
methods.来自Why(令人心酸的)Ruby 指南:
From Why's (poignant) guide to ruby:
对于具有 C# 背景(或其他语言)背景的任何人来说,这可能会有所帮助:
Ruby 块就像 C# 中的 lambda 表达式和匿名方法。它们就是 C# 所说的委托(Ruby 称之为 Procs),也就是说它们本质上是可以作为值传递的函数。在 Ruby 和 C# 中,它们也可以表现为闭包。
红宝石: <代码>{ |x| x + 1 }
C#:
x => x + 1
Ruby:
{ |name|放置“你好#{name}”}
C#:
name => { Console.WriteLine("你好 {0}", name); 。
C# 和 Ruby 都提供了编写上述示例的替代方法
Ruby:
C#:
在 Ruby 和 C# 中,都允许使用多个语句。在 Ruby 中,需要使用上面的第二种语法。
这些概念在许多其他受到函数式编程背后的思想影响的语言中也可用。
For anybody coming to this question from a C# background (or other langs really), this might help:
Ruby blocks are like lambda expressions and anonymous methods in C#. They are what C# calls delegates (and Ruby calls Procs), which is to say that they are essentially functions that can be passed as values. In both Ruby and C#, they can also behave as closures.
Ruby:
{ |x| x + 1 }
C#:
x => x + 1
Ruby:
{ |name| puts "Hello there #{name}" }
C#:
name => { Console.WriteLine("Hello there {0}", name); }
Both C# and Ruby offer alternative ways to write the above example.
Ruby:
C#:
In both Ruby and C#, multiple statements are allowed, In Ruby, the second syntax above is required for this.
These concepts are available in many other languages that have been influenced by the ideas behind functional programming.
《Programming Ruby》一书有很棒的块的说明以及使用它们。
在 1.9+ 中,传递到块中的参数列表变得更加复杂,允许定义局部变量:
;c,d
在块内声明两个新的局部变量,它们不接收来自被调用的值-routine 的yield
语句。 Ruby 1.9+ 保证,如果变量存在于块之外,则它们不会被块内的同名变量所影响。这是新的行为; 1.8会踩在他们身上。还有“splat”运算符
*
,它在参数列表中工作:将多个值中的第一个分配给“a”,其余所有值将在“b”中捕获,这将是像数组一样处理。
*
可以位于a
变量上:将捕获除最后一个变量之外的所有传入变量,该变量将传递给
b
。并且,与前两个类似:将第一个值分配给
a
,将最后一个值分配给c
,并将所有/任何中间值分配给b
。我认为这非常强大且流畅。
例如:
The book "Programming Ruby" has a great explanation of blocks and using them.
In 1.9+, the parameter list passed into a block became more sophisticated, allowing local variables to be defined:
;c,d
declare two new local variables inside the block, that do not receive values from the called-routine'syield
statement. Ruby 1.9+ guarantees that, if the variables existed outside the block, that they will not be stomped on by the same-named variables inside the block. This is new behavior; 1.8 would stomp on them.There's also the "splat" operator
*
, which works in the list of parameters:Would assign the first of multiple values to "a", and all the rest would be captured in "b" which would be treated like an array. The
*
could be on thea
variable:would capture all passed in variables except the last one, which would be passed to
b
. And, similarly to the two previous:would assign the first value to
a
, the last value toc
and all/any intervening values tob
.I think that's pretty powerful and slick.
For instance:
块是匿名一等过程的轻量级文字,具有一些恼人的限制。它们在 Ruby 中的工作方式与在几乎所有其他编程语言中的工作方式相同,模数上述限制,即:
Blocks are lightweight literals for anonymous first-class procedures with some annoying limitations. They work the same way in Ruby as they work in pretty much every other programming language, modulo the afore-mentioned limitations, which are:
块是 Ruby 中对代码进行分组的一种方式。有两种写块的方法。一种是使用 do..end 语句,另一种是将代码括在大括号中:{}。块在 Ruby 编程语言中被视为对象,默认情况下所有函数都接受隐式块参数。
以下是执行相同操作的块的两个示例:
块可以接收竖线 || 内以逗号分隔的参数列表。例如:
块(在 ruby 1.9.2 中)可以显式拥有局部变量:
局部变量可以与参数组合:
所有函数都可以接收默认块参数:
do..end 和 {} 块之间有什么区别?按照约定,{} 块位于单行上,而 do..end 块跨越多行,因为这样更容易阅读。主要区别与优先级有关:
Blocks are a way of grouping code in Ruby. There are two ways to write blocks. One is using the do..end statement and the other is surrounding the code in curly braces: {}. Blocks are considered objects in the Ruby programming language, and by default all functions accept an implicit block argument.
Here are two examples of blocks that do the same thing:
Blocks can receive lists of comma-separated arguments inside vertical bars ||. For example:
Blocks (in ruby 1.9.2) can explicitly have local variables:
Local variables can be combined with parameters:
All functions can receive a default block argument:
What is the difference between do..end and {} blocks? By convention {} blocks are on a single line and do..end blocks span multiple lines, since they are each easier to read this way. The main difference has to do with precedence though: