Phr*_*ogz 36
我从这个答案中提出了我自己的解释,稍作修改:
Ruby中的"块"与通用编程术语"代码块"或"代码块"不同.
假装以下(无效)Ruby代码实际工作:
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"
Run Code Online (Sandbox Code Playgroud)
虽然该代码无效,但它的意图 - 将一些代码传递给方法并使该方法运行代码 - 在Ruby中可以通过各种方式实现.其中一种方法是"块".
Ruby中的Block非常非常像一个方法:它可以采用一些参数并运行代码.每当你看到foo{ |x,y,z| ... }或者foo do |x,y,z| ... end,那些是带有三个参数并运行...它们的块.(您甚至可能会看到upto上面的方法正在传递一个块.)
因为Blocks是Ruby语法的特殊部分,所以允许每个方法传递一个块.方法是否使用块取决于方法.例如:
def say_hi( name )
puts "Hi, #{name}!"
end
say_hi("Mom") do
puts "YOU SUCK!"
end
#=> Hi, Mom!
Run Code Online (Sandbox Code Playgroud)
上面的方法传递了一个准备发出侮辱的块,但由于该方法从不调用块,因此只打印好消息.以下是我们如何从方法中调用块:
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.
Run Code Online (Sandbox Code Playgroud)
我们block_given?用来查看是否传递了一个块.在这种情况下,我们将一个参数传递回块; 这取决于你的方法来决定传递给块的内容.例如:
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?
Run Code Online (Sandbox Code Playgroud)
它只是一个约定(一个好的,一个你想要支持的),一些类将刚刚创建的实例传递给块.
这不是一个详尽的答案,因为它不包括捕获块作为参数,它们如何处理arity,在块参数中解除映射等等,但是打算用作Blocks-Are-Lambdas简介.
mae*_*ics 27
Ruby块是一种创建Proc对象的方法,这些对象代表可供其他代码使用的代码.Proc对象是花括号{}(或多do...end行块的短语,其优先级低于花括号)之间的指令,可以选择接受参数并返回值(例如{|x,y| x+y}).Proc是第一类对象,可以显式构造或隐式获取方法伪参数:
构造为Proc对象(或使用lambda关键字):
add1 = Proc.new {|x| x+1} # Returns its argument plus one.
add1.call(1) # => 2
Run Code Online (Sandbox Code Playgroud)作为方法伪参数传递,显式使用特殊的&last-argument语法sugar运算符或隐式使用block_given?/ yieldpair:
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.
Run Code Online (Sandbox Code Playgroud)第二种形式通常用于访客模式 ; 数据可以作为call或yield方法的参数传递给特殊块参数.
nun*_*nia 20
任何由花括号包围的代码都是一个块.
2.times { print "Yes, I've used chunky bacon in my examples, but never again!" }就是一个例子.使用块,您可以将一组指令组合在一起,以便可以在程序中传递它们.花括号给出了蟹钳的外观,这些钳子抓住了代码并将它们固定在一起.当你看到这两个钳子时,请记住里面的代码被压入一个单元.这就像他们在购物中心出售的那些装有小铅笔和微观纸的小Hello Kitty盒子,所有这些盒子都塞进一个闪闪发光的透明盒子里,可以隐藏在你的手掌中进行隐蔽的静止操作.除了块不需要那么多眯眼.花括号也可以用单词do和end来交换,如果你的块长于一行,那就很好了.
loop do
print "Much better."
print "Ah. More space!"
print "My back was killin' me in those crab pincers."
end
Run Code Online (Sandbox Code Playgroud)
块参数是由管道字符包围并用逗号分隔的一组变量.
|x|, |x,y|, and |up, down, all_around| are examples.
Run Code Online (Sandbox Code Playgroud)
块参数在块的开头使用.
{ |x,y| x + y }
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,| x,y | 是争论.在论证之后,我们有一些代码.表达式x + y将两个参数加在一起.我喜欢将管道字符视为代表隧道.它们给出了滑槽的外观,变量正在向下滑动.(一个x向下蔓延的鹰,而y整齐地穿过她的腿.)这个滑道充当了街区和周围世界之间的通道.变量通过此滑槽(或隧道)传递到块中.
对于任何从C#背景(或其他langs)来到这个问题的人来说,这可能会有所帮助:
Ruby块就像lambda表达式和C#中的匿名方法.它们是C#调用委托(和Ruby调用Procs),也就是说它们本质上是可以作为值传递的函数.在Ruby和C#中,它们也可以表现为闭包.
红宝石: { |x| x + 1 }
C#: x => x + 1
红宝石: { |name| puts "Hello there #{name}" }
C#: name => { Console.WriteLine("Hello there {0}", name); }
C#和Ruby都提供了编写上述示例的替代方法.
红宝石:
do |name|
puts "Hello there #{name}"
end
Run Code Online (Sandbox Code Playgroud)
C#:
delegate(string name)
{
Console.WriteLine("Hello there {0}", name);
}
Run Code Online (Sandbox Code Playgroud)
在Ruby和C#中,允许多个语句,在Ruby中,上面的第二个语法是必需的.
许多其他语言都提供了这些概念,这些语言受到函数式编程背后的思想的影响.
在1.9+中,传递到块中的参数列表变得更加复杂,允许定义局部变量:
do |a,b;c,d|
some_stuff
end
Run Code Online (Sandbox Code Playgroud)
;c,d在块内声明两个新的局部变量,它们不接收来自被调用例程yield语句的值.Ruby 1.9+保证,如果变量存在于块之外,则它们不会被块内的同名变量所踩踏.这是新的行为; 1.8会踩到它们.
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
Run Code Online (Sandbox Code Playgroud)
还有"splat"运算符*,它在参数列表中起作用:
do |a,*b|
some_stuff
end
Run Code Online (Sandbox Code Playgroud)
将多个值中的第一个分配给"a",其余所有值都将在"b"中捕获,这将被视为一个数组.在*可能的a变量:
do |*a,b|
some_stuff
end
Run Code Online (Sandbox Code Playgroud)
将捕获所有传入的变量,但最后一个变量将被传递给b.而且,与前两个类似:
do |a,*b,c|
some_stuff
end
Run Code Online (Sandbox Code Playgroud)
将第一个值分配给a,将最后一个值分配给c所有/任何中间值b.
我认为这非常强大而且光滑.
例如:
def blah
yield 1,2,3,4
end
blah { |a, *b| puts "a: #{a}", "b: #{b.join(',')}" }
# >> a: 1
# >> b: 2,3,4
blah { |*a, b| puts "a: #{a.join(',')}", "b: #{b}" }
# >> a: 1,2,3
# >> b: 4
blah { |a, *b, c| puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}" }
# >> a: 1
# >> b: 2,3
# >> c: 4
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
25238 次 |
| 最近记录: |