Is Ruby pass by reference or by value?这个问题吸引了很多有用的答案,也引起了很多分歧。到目前为止,我在任何一个答案中都没有看到任何解释以下内容的东西:
ruby -e "def f(x) x=7 end; a=3; f(a); print a"打印3。
ruby -e "def f(x) x[0]=7 end; a=[3]; f(a); print a[0]"打印7。
从经验上看,在我看来,标量对象和更复杂的对象(如散列和数组)之间有某种区别,标量是通过值传递的,而复杂对象是通过引用传递的。这与C语言的语义相似。
我的理解是,ruby中的所有东西都是一个对象,前面的问题的答案中没有一个提到标量和复杂类型之间的区别。那么,我的描述是错误的,如果是的话,更好的描述是什么?
发布于 2017-01-24 18:42:05
这里的术语的问题是Ruby是“通过对象引用传递”,这是在其他语言中表示“指向对象的指针”的一种方式。Ruby中的指针和引用之间的界线是模糊的,因为没有实际的指针,另外,通过引用计数将对象本身保存在内存中,而指针最终是引用。因此,它们是指向对象的指针,而不是传统意义上的引用,即硬链接到同一个变量。
根据定义,每个变量总是表示一个对象,即使它没有定义:nil本身既是一个对象,也是一个数字,甚至是浮点对象。这使得术语“标量”几乎无关紧要,Ruby中没有其他语言中的基本类型,布尔值、数字、字符串和类实例之间的区别非常模糊。
一般规则是,您永远无法将更改反向传播到变量,但是通过方法所做的更改确实会传播。要理解为什么,下面是Ruby如何看待您的代码:
def f(x)
# Change value of local variable x to 7
x = 7
end它只是重新定义了x所指向的对象,因为甚至7也是一个对象。
另一段代码在Ruby的感知方式上完全不同:
def f(x)
# Send the []= method call to x with the argument 7
x.send(:[]=, 7)
end这将向x发送消息(方法调用)以触发[]=方法。该方法可以使用该值执行它想做的任何事情,但对于数组、散列和具有特定意义的复数,则是如此。它更新对象x引用的内部状态。
您可以看到这在其他场景中是如何发生的:
def f(x)
x += 'y'
end这将扩展到x = x + y,它使用中间结果进行变量重分配。未修改原始x值。
def f(x)
x << 'y'
end在本例中,是x.send(:<<, 'y')对x进行了就地修改,因此修改了原来的内容。
在编写和理解Ruby代码时,能够识别方法调用是一件很重要的事情。有时他们甚至没有那么明显。您可能认为=的存在意味着“变量赋值”,但严格说来并非如此:
def f(x)
x.y = 'z'
end这看起来像是分配给了y属性的x,但它不是,它只是调用y=方法,它等同于x.send(:y=, 'z'),这是x可以用多种方式解释的东西。这可能会修改值,或者它可能会做一些完全不同的事情。如果不更仔细地理解x,就不可能知道这一点。
发布于 2017-01-24 23:06:05
从经验上看,在我看来,标量对象和更复杂的对象(如散列和数组)之间有某种区别,标量是通过值传递的,而复杂对象是通过引用传递的。
Ruby中没有“标量对象”或“复杂对象”这样的东西。一切都是物体。句号。每件事都是按值传递的,永远没有例外。从未有过任何参照,从来没有。
更准确地说,Ruby通常被称为逐对象调用、按对象调用或逐对象调用。这是传递值的特殊情况,传递的值始终是指向对象的指针。
闭包中的自由变量是通过引用捕获的,但这是一个不同的问题,与此无关。
这与C语言的语义相似。
不,实际上是不会的。C中没有按引用传递,C总是按值传递,就像Ruby一样。
在C中,一切都是通过值传递的。ints是通过值传递的。chars是通过值传递的。指针是通过值传递的。Ruby与C类似,只是只有指针;传递的每个值都是指向对象的指针。
f(x) x=7端a=3f(A)a #=> 3 def (X)x=7端a=3f(A)a #=> 7
这两种情况根本不同:在第一种情况下,将一个新值绑定到方法中的参数x。此重新绑定仅在方法体中可见。方法参数本质上表现为局部变量。(实际上,如果您考虑方法体的局部变量,就会看到参数会显示出来。)
在第二种情况下,您调用一个改变接收方的方法。没有作业在进行。是的,有一个等号,但这只是Ruby的索引方法分配语法糖的一部分。您真正要做的是调用方法[]=,将0和7作为参数传递。它完全等同于调用x.[]=(0, 7);实际上,如果需要,可以用这种方式编写它。(试试!)也许,如果您使用的是 method而不是[]=,或者另一种方法的名称更明显地是“我正在更改数组”,比如clear或replace,您可能就不会那么困惑了。
数组仍然是您传递给方法的完全相同的数组。这一提法未作修改。只有数组是。如果我们不能将东西插入到数组中,那么数组将是非常无用的,然后这些东西就会留在那里!
因此,这两种情况的不同之处在于,在第一种情况下,您分配了一个新值,即您修改了引用,这是不起作用的,因为Ruby是按值传递的。在第二种情况下,您修改了这个值,因为Ruby不是一种纯函数语言,它具有完全不可变的对象。Ruby是不纯的,它确实有可变的对象,如果您改变了一个对象,那么这个对象就会发生变异。
我妈妈和我的理发师用不同的名字称呼我,但是如果我的理发师给我理发,我妈妈也会注意到这个事实。
注意:有些对象没有改变它们的方法。这些对象是不可变的。Integer是如此不可变的对象,所以您永远无法在Integers中演示类似的东西,但这纯粹是由于Integers没有变异方法这一事实,这与它们是“标量”无关。您可以有复杂的复合对象,这些对象没有任何变异方法,如果需要:here is a question about implementing a linked list in Ruby,这两个答案包含三个链表实现,它们都是不可变的。(免责声明:有两个实现的一个答案来自我。)
发布于 2017-01-24 17:33:22
Ruby是“传递到对象的指针”。
那么现在有什么区别呢?
def f(x)
x = 7
end为局部变量分配一个新值,因为您重新分配了一个局部变量,因此x-this更改是本地的。
def f(x)
x[0] = 7
end为x引用的第一个元素分配一个新值,-this更改是全局的,因为您修改了一个对象。
按值传递和引用传递之间的区别不适用于Ruby,即来自另一种编程语言,在Ruby的上下文中没有意义。
https://stackoverflow.com/questions/41834229
复制相似问题