Ruby中的块与Smalltalk的比较

11

Ruby 中的 block 是什么意思?它看起来与 Smalltalk 相似,但你不能向其发送消息。

例如,在 Smalltalk 中:

[:x | x + 3] value: 3

返回 6。但在 Ruby 中:

{|x| x + 3}.call 3

会导致SyntaxError。

不过在Ruby中,你可以向lambda传递消息:

irb(main):025:0> ->(x){x+3}.call 3
=> 6
在Ruby中,块不是块,但lambda是块?这是真的吗?我的意思是,Ruby lambda和Smalltalk block之间有什么区别吗?如果是这样,那么Ruby块是什么?
更新:
根据下面的评论和答案以及一些搜索,我对Ruby块有了更多的理解。在Ruby中,通常一段代码会评估一个值,而每个值都是一个对象。但是,块不评估值,因此它不是一个对象。相反,它可以作为对象的一部分来使用。例如,在{|x| x + 3}中,它可以作为proc {|x| x + 3}对象的一部分。
但这让我感到困惑。在Smalltalk中,几乎每个表达式都可以分解为对象(绑定到变量的除外)。看起来在Ruby中有更多的例外情况。

我相信你缺少了一个yield。不过我在块上的知识不是特别丰富。 - Earlz
感谢指出yield。我找到了这个相关的问题:https://dev59.com/uHRA5IYBdhLWcg3w9y10 - weakish
3个回答

15

首先,Ruby 块(block)最重要的一点是:它不是一个对象。它是一种语法结构,并且显然有一个等效的实现 - 但它不是一个对象,因此不能接收消息。这使你的例子

{|x| x + 3}.call 3

语法不正确。Lambda表达式,procs - 都是封装代码块的对象,并且有一个call方法来执行该代码块。

因此,代码块只是可以传递给方法的一段代码 - 没有更多或更少。如果您将其传递给Proc.new构造函数,它将对其进行封装并给您一个可以处理的对象:

Proc.new {|x| x + 3}.call 3

我曾错误地认为 Ruby 块是我们作为参数传递给方法的某个对象。感谢您澄清了这一点。 - weakish
所以如果我理解正确,这也意味着当您将Ruby块作为参数传递给方法时,VM会以某种方式将其包装到Proc.new中? - mathk
我不知道具体细节,但我相信那是不正确的 - 除非你在后面加上 & 标记。隐式 block(无需包装):def yield3; yield 3; end; yield3() { |x| puts x }; - 显式 proc(没有传递 block):def callprocwith3(func); func.call(3); end; callprocwith3(Proc.new {|x|puts x}) - 转换为 proc(包装一个 block):def callblockwith3(&func); func.call(3); end; callblockwith3() { |x| puts x }; - 它们大致完成相同的工作,但处理方式不同。我可以省略空括号 (),但这样更清楚哪些是参数,哪些不是。 - Amadan
@mathk 不,你不能将 Ruby 块作为参数传递给方法。Ruby 会报告错误,而不是自动将块转换为 Proc 实例。这很奇怪。但在 Ruby 中,块不是一个对象。你不能将其作为参数传递给方法。我认为最好避免使用“发送块”这个词,因为你不能“发送”它(就像发送对象一样)。将块视为某种关键字或语法糖,用于将一段代码与其他内容相关联可能更好。至少这样考虑可以让我的大脑更加舒适。 - weakish

1
一个精度:
我甚至会说,在Smalltalk中,绑定甚至是由对象组成的。想想MethodContext。实际上,你正在做的是将对象存储在MethodContext中。所以
a := Object new

可以重写为:

thisContext at: 1 put: Object new.

但显然你不会这样写,因为你需要知道temps变量在哪里。


1
在Smalltalk中,一个Block是一个匿名对象。语法上,它由一对[]括起来。
当被评估时,它将返回其中最后一个被评估的表达式,并且其协议中有许多方法。
以下是Smalltalk(在本例中为Dolphin Smalltalk 6.03 Community Edition)中Blocks的类注释:
“Blocks封装了一系列要在以后执行的语句。块可以捕获(或“关闭”)运行时状态,例如在创建它们的封闭词法范围内的临时变量的值。当评估块时,它会像在定义它的词法范围内一样执行,除了块可能具有在评估时绑定的参数。块可以作为消息的参数传递给其他对象,并在适当时由这些对象评估,因此形成了一个非常强大和通用的“可插入性”机制,这是Smalltalk提供大部分功能的核心特征。”
相比之下,在Ruby中,一个Block只是一个参数字符串。它的语法由一对{}括起来,但它没有自己的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接