Ruby 引用和值传递

Ruby Reference & Value pass by

提问人:Nikkolasg 提问时间:12/9/2014 更新时间:12/9/2014 访问量:72

问:

我知道有很多类似的问题被问到。我已经阅读了它们,但仍然不明白为什么我的代码会这样做。它仅用于训练,我已经实现了一个简单的 LinkedList(单个链接),我想实现两个这样的排序列表的合并。代码如下

#!/usr/bin/ruby
#
module LinkedList

    class Node

        attr_accessor :next,:data

        def to_s
            s = @data.to_s + " -> "
            s += @next.to_s if @next
            s
        end
    end

end
def append tail, node
    tail.next = node
    tail = node
    node = node.next
end

def merge_lists2 list1, list2
    tail = LinkedList::Node.new
    while(list1 && list2)
        list1.data <= list2.data ? list1 = append(tail,list1) : list2 = append(tail,list2)
        puts list1
        puts list2
        puts tail
        puts "#######################################################"
        STDIN.gets
    end
    if list1
        tail.next = list1
    elsif list2
        tail.next = list2
    end
    tail.next
end
def generateList times = 3,factor = 1
    list = LinkedList::Node.new
    list.data = 0
    curr = list
    (1..times).each do |i|
        curr.next = LinkedList::Node.new
        curr = curr.next
        curr.data = i*factor
    end
    list
end

def main
    l1 = generateList 4,2
    l2 = generateList 5,1.5
    puts l1
    puts l2
    l3 = merge_lists2 l1,l2
    puts l3
end

以下代码是我的“旧”版本:

def append tail, node
    tail.next = node
    tail = node
    node = node.next
end

def merge_lists2 list1, list2
    tail = LinkedList::Node.new
    while(list1 && list2)
        list1.data <= list2.data ? append(tail,list1) : append(tail,list2)

在旧版本中,它以无限循环运行,因为 list1 和 list2 从未更新过,因此 while 循环一直在进行。它们从未更新过,因为 list1 和 list2 变量被分配给 append 函数中的不同对象,因此当从函数返回时,list1 和 list2 会“收回”它们的原始引用。 但是“尾巴”参考更新了!!你可以通过运行代码看到它,你会看到 tail 始终是新合并列表的“tail”,而不是新列表的“head”。我不明白为什么,因为 tail 也在 append 中引用了一个新对象,所以当从函数返回时,“tail”应该仍然是“head”(哈哈,这太疯狂了)

谢谢。。。

Ruby 按引用传递

评论


答:

0赞 tadman 12/9/2014 #1

Ruby 的问题在于它向你隐瞒了很多东西。对象被传入,看似是引用,但实际上是巧妙伪装的指针。当您“取消引用”指针时,您可以操作原始数据,这与引用不同。与C++不同,Ruby不会特意提醒你这是这种情况。

因此,这意味着方法的参数只是行为类似于指向对象的指针的变量。将它们更改为其他值不会影响原始值。对它们调用方法或更改属性确实会影响原始方法,它们已被取消引用。

这里的实现存在很多问题。首先,像这样的方法应该是 Node 类的一部分:append

class Node
  def append(list)
    node = @data

    while (node)
      if (node.next)
        node = node.next
      else
        node.next = list
        return self
      end
    end
  end
end

只要你没有循环列表,这应该可以工作。请注意,该值永远不会被修改,它只是集成的。list

请注意,此方法适用于单个节点(一个元素列表)和由多个节点组成的较长列表。通常,在编程中,您会尽量避免处理特殊情况,因此这会最大限度地减少编写和正常工作所需的代码。append

评论

3赞 Uri Agassi 12/9/2014
...这与 Java、C# 等中的行为相同。在某些语言中,您可以明确表示要通过引用传递(例如使用),但默认情况下,您将指针传递到值,这意味着设置它们(而不是操作它们)不会更改调用方的值ref String myVar
0赞 Nikkolasg 12/9/2014
我知道在常规的良好实现中,append 应该是 Node 类的一部分。然而,这不是我想要的;)因为我想要的是我的“尾巴”变量,总是指向新列表的头部(打印后)!如果我使用您的代码,tail 将始终指向列表的 TAIL。而且你的解释没有回答为什么每次我调用我的函数“append”时尾部可变指向不同的对象......(由于我使尾巴指向函数中的不同对象,因此调用者不应该看到差异..但它确实如此!
0赞 tadman 12/9/2014
更改局部变量在该块之外没有影响。要使任何内容永久化,您需要操作 a) 传入的对象的属性(例如),或 b) 局部或类级类型的实例变量(例如)。您尝试重新分配并使其反向传播将不起作用。相反,试着重新调整我在这里给出的示例,以获得你想要的行为。tail.next@@tailtail
0赞 Nikkolasg 12/9/2014
所以在我的函数追加中,“tail”变量被更新(在调用者中),因为我做了“tail.next = node”?我以为由于函数中的“tail = node”,调用方中的“tail”变量将保持不变。