我对 ruby 相当陌生,但有 C/C++ 背景,我相信我对计算机和编程的工作原理有基本了解。
在 C++ 中,如果您想创建一个指向某个值的指针,您可以将其指定为指针:http://www.cplusplus.com/doc/tutorial/pointers/。
例子:
int x = 3;
int *p = &x;
*p = 4;
std::cout << x;
Run Code Online (Sandbox Code Playgroud)
这对我来说很有意义,因为现在p指向 的地址x,因此当我们更改 的值时p,x也会发生变化。x在这种情况下将输出 4。
但是当我跑步时:
a = []
b = a
a[0] = 3
p b
# => [3]
c = "this is a string"
d = c
c.upcase!
c = "the string has been edited"
p d
# => THIS IS A STRING
Run Code Online (Sandbox Code Playgroud)
在红宝石中,b输出[3]对我来说不应该输出任何内容。并且d输出"THIS IS A STRING"而不是"the string has been edited"。Ruby 和指针是另一个问题,其中介绍了如何在 Ruby 中使用指针。
我的问题是:为什么数组在 ruby 中被视为指针以及它是如何工作的?是像C++中那样b指向 的a地址吗?是什么让变量改变它所指向的内容?例如:如果a = b什么时候a != b?为什么整数不指向另一个变量的地址?
为什么数组在 ruby 中被视为指针以及它是如何工作的?
他们不是。Ruby 中没有“指针”这样的东西。
是像C++中那样
b指向 的a地址吗?
Ruby 中没有“地址”这样的东西。语言规范规定,当您取消引用变量时,它将计算分配给该变量的最后一个对象。语言实现如何做到这一点是完全无关的,你不应该,事实上也不可能知道它。它可以使用指向内存位置的指针。它可以使用字典。它可以将价值打印在纸上,然后传真到东亚的一家血汗工厂,由童奴对其进行解读。(这将是非常不道德、不道德和非法的,但它是一个完全有效的实施。)
是什么让变量改变它所指向的内容?
赋值,并且只有赋值才能改变变量的绑定。
例如:如果
a = b什么时候a != b?
当!=方法被重写时这样说。这将是一个糟糕的主意,但尽管如此:
class Foo
def !=(*)
false
end
end
a = Foo.new
b = a
a != b #=> false
Run Code Online (Sandbox Code Playgroud)
为什么整数不指向另一个变量的地址?
同样,Ruby 中不存在“指针”或“地址”这样的东西,因此,整数永远不能“指向”“地址”,仅仅是因为“指向”和“地址”不是存在于 Ruby 中的概念。红宝石。
让我们逐行查看您的代码:
a = []
Run Code Online (Sandbox Code Playgroud)
您创建一个新Array对象并将局部变量绑定a到该新数组对象。
b = a
Run Code Online (Sandbox Code Playgroud)
现在,您将局部变量绑定b到表达式 的求值结果a。计算仅包含变量名称的表达式的结果是什么?该变量被取消引用,即它计算为先前绑定到该变量的对象。
在本例中,该对象是Array您在第 1 行创建的对象。
a[0] = 3
Run Code Online (Sandbox Code Playgroud)
该行只是以下行的语法糖:
a.[]=(0, 3)
Run Code Online (Sandbox Code Playgroud)
[]=在这里,您将发送传递两个参数的消息0以及3取消引用局部变量的结果a。同样,取消引用局部变量的a结果是该变量绑定到最后一个变量,即第Array1 行。该方法Array#[]=被定义为改变其接收者,以便将其第二个参数放置在第一个参数指定的索引处。
该方法不必改变其接收者。它还可以返回Array. (尽管在这种特殊情况下,这将是无用的,因为a[0] = 3语法糖的行为类似于赋值,因此将始终评估右侧。换句话说,Array#[]=除非您使用显式方法调用语法,否则将忽略 的返回值a.[]=(0, 3)。)
p b
Run Code Online (Sandbox Code Playgroud)
在这里,我们再次取消引用局部变量b,它仍然指向完全相同的对象(从来没有任何重新分配),它仍然是来自Array第 1 行的对象。
Array整个代码中只有一首单曲。您从未创建过第二个,也从未调用过会返回新的方法Array。
一种确实返回新值Array而不是改变接收器的方法是 method Array#+。这是一个使用的替代示例Array#+:
a = []
b = a
a += [3]
Run Code Online (Sandbox Code Playgroud)
a += [3]相当于a = a + [3]相当于a = a.+([3])。因此,我们取消引用a,它给出了第 1 行的 ,然后我们向它发送带有单个参数 的Array消息。定义为返回一个新的,它是接收者和参数的串联。最后,我们将这个 new 赋给局部变量。+[3]Array#+ArrayArraya
p b
Run Code Online (Sandbox Code Playgroud)
局部变量b仍然绑定到同一个对象,因为我们从未重新分配它。因此,它仍然绑定到Array第 1 行。我们从未更改过此内容Array,因此,此打印[]。
关于你的第二个例子(这次我会更快一点):
c = "this is a string"
Run Code Online (Sandbox Code Playgroud)
创建一个新的String,分配给c.
d = c
Run Code Online (Sandbox Code Playgroud)
绑定d到任何c绑定的内容,即String来自第 1 行的内容。
c.upcase!
Run Code Online (Sandbox Code Playgroud)
将消息发送upcast!到String通过取消引用获得的c。String#upcase!被定义为改变接收者。因此,String两者都引用了 that c,d现在已经改变了本身。
c = "the string has been edited"
Run Code Online (Sandbox Code Playgroud)
String创建一个与另一个无关的全新并将其分配给c.
p d
Run Code Online (Sandbox Code Playgroud)
d从未被重新分配过,因此,它仍然绑定到String第 1 行。