TL;博士
在您现在编辑的原始问题中,您将递归与变异和传播混淆了。这三个概念都是在正确的情况下以及预期行为时有用的工具。您可能会发现您发布的特定示例令人困惑,因为您不希望字符串在适当的位置发生变异,或者更改传播到指向该对象的所有指针。
泛化方法的能力是在 Ruby 等动态语言中实现鸭子类型的原因。主要的概念障碍是理解变量指向对象,只有使用核心库和标准库的经验才能让您了解对象如何响应特定消息。
Ruby 中的字符串是响应消息的成熟对象,而不是简单的语言原语。在接下来的部分中,我将尝试解释为什么这很少会成为问题,以及为什么该功能在像 Ruby 这样的动态语言中很有用。我还介绍了一种产生您最初期望的行为的相关方法。
都是关于对象分配的
我的问题是为什么会这样。我知道设置“b = a”使它们成为相同的object_id,因此从技术上讲,同一个变量字符串有两个名称。
这在日常编程中很少出现。考虑以下:
a = 'foo' # assign string to a
b = a # b now points to the same object as a
b = 'bar' # assign a different string object to to b
[a, b]
#=> ["foo", "bar"]
这按您期望的方式工作,因为变量只是对象的占位符。只要您将对象分配给变量,Ruby 就会按照您的直觉进行操作。
对象接收消息
在您发布的示例中,您遇到了这种行为,因为您真正在做的是:
a = 'foo' # assign a string to a
b = a # assign the object held in a to b as well
b.replace 'bar' # send the :replace message to the string object
在这种情况下,String#replace正在向a和b指向的同一对象发送消息。由于两个变量都包含同一个对象,因此无论您调用方法 asa.replace
还是,都会替换字符串b.replace
。
这可能不直观,但在实践中很少出现问题。在许多情况下,这种行为实际上是可取的,这样您就可以传递对象而无需关心方法如何在内部标记对象。这对于泛化方法或自记录方法的签名很有用。例如:
def replace_house str
str.sub! 'house', 'guard'
end
def replace_cat str
str.sub! 'cat', 'dog'
end
critter = 'house cat'
replace_house critter; replace_cat critter
#=> "guard dog"
在此示例中,每个方法都需要一个 String 对象。它不在乎字符串在其他地方被标记为critter;在内部,该方法使用标签str来引用同一个对象。
只要你知道一个方法什么时候改变了接收者,什么时候它传回了一个新的对象,你就不会对结果感到惊讶。稍后再详细介绍。
String#replace 真正的作用
在您的具体示例中,我可以看到String#replace的文档可能会令人困惑。文档说:
replace(other_str) → str
将 str
的内容和污点替换为 other_str 中对应的值。
这实际上意味着b.replace
改变对象(“替换内容”),而不是返回新对象以分配给变量。例如:
# Assign the same String object to a pair of variables.
a = 'foo'; b = a;
a.object_id
#=> 70281327639900
b.object_id
#=> 70281327639900
b.replace 'bar'
#=> "bar"
b.object_id
#=> 70281327639900
a.object_id == b.object_id
#=> true
请注意,object_id 永远不会改变。您使用的特定方法重用了相同的对象;它只是改变了它的内容。将此与返回对象副本的String#sub等方法进行对比,这意味着您将返回具有不同 object_id 的新对象。
怎么做:分配新对象
如果你想让a和b指向不同的对象,你可以使用像String#sub这样的非变异方法:
a = 'foo'; b = a;
b = b.sub 'oo', 'um'
#=> "fum"
[a.object_id, b.object_id]
#=> [70189329491000, 70189329442400]
[a, b]
#=> ["foo", "fum"]
在这个相当人为的示例中,b.sub
返回一个新的String 对象,然后将其分配给变量b。这导致将不同的对象分配给每个变量,这是您最初期望的行为。