Ruby块的最佳解释?

Bla*_*man 38 ruby block

您可以分享的Ruby块的最佳解释是什么?

使用和编写代码都可以阻塞?

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是第一类对象,可以显式构造或隐式获取方法伪参数:

  1. 构造为Proc对象(或使用lambda关键字):

    add1 = Proc.new {|x| x+1} # Returns its argument plus one.
    add1.call(1) # => 2
    
    Run Code Online (Sandbox Code Playgroud)
  2. 作为方法伪参数传递,显式使用特殊的&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)

第二种形式通常用于访客模式 ; 数据可以作为callyield方法的参数传递给特殊块参数.

  • 大括号具有很高的优先级; `do`优先级低.如果方法调用具有未括在括号中的参数,则块的大括号形式将绑定到最后一个参数,而不是整个调用.`do`表单将绑定到调用. (4认同)
  • 拜托英语!......"Ruby块是Proc对象的语法文字...." - 如果人们不知道块是什么,我猜他们不会知道什么"Proc对象的语法文字"意味着.尝试解释好像读者是5岁. (2认同)

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整齐地穿过她的腿.)这个滑道充当了街区和周围世界之间的通道.变量通过此滑槽(或隧道)传递到块中.

  • "任何用花括号包围的代码都是一个块",除非它是**哈希**. (18认同)

Jus*_*tin 7

对于任何从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中,上面的第二个语法是必需的.

许多其他语言都提供了这些概念,这些语言受到函数式编程背后的思想的影响.


the*_*Man 6

" 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)