【问题标题】:Ruby Reference & Value pass byRuby 参考和价值传递
【发布时间】:2015-02-06 09:37:12
【问题描述】:

我知道有人问过很多类似的问题。我已经阅读了它们,但仍然不明白为什么我的代码会这样做。它仅用于培训,我已经实现了一个简单的 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 还引用了 append 的新对象,所以当从函数返回时,“tail”应该仍然是“head”(哈哈,这太疯狂了)

谢谢...

【问题讨论】:

    标签: ruby pass-by-reference pass-by-value


    【解决方案1】:

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

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

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

    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 方法适用于单个节点(一个元素列表)和由多个节点组成的较长列表。通常在编程中,您会尽量避免处理异常情况,这样可以最大限度地减少您需要编写和正常工作的代码。

    【讨论】:

    • ...这与 Java、C# 等中的行为相同。在某些语言中,您可以明确说您想通过引用传递(例如使用 ref String myVar) ,但默认情况下,您将指针传递给值,这意味着设置它们(而不是操纵它们)不会更改调用者的值
    • 我知道在常规的良好实现中追加应该是 Node 类的一部分。但是,这不是我想要的;)因为我想要的是我的“尾部”变量,始终指向新列表的头部(在之后打印)!如果我使用您的代码,tail 将始终指向列表的 TAIL。你的解释没有回答为什么每次我调用我的函数“附加”时尾部变量指向不同的对象......(因为我让尾部指向函数中的不同对象,调用者不应该看到差异......但是它是!!)
    • 更改局部变量在该块之外没有任何影响。要使任何东西永久化,您需要操作 a) 传入对象的属性(例如 tail.next),或 b) 本地或类级别 @ 类型的实例变量(例如 @tail)。您尝试重新分配 tail 并使其反向传播将不起作用。相反,请尝试对我在此处给出的示例进行重组,以获得您想要的行为。
    • 所以在我的函数追加中,“tail”变量被更新(在调用者中),因为我做了“tail.next = node”?我认为由于函数中的“tail = node”,调用者中的“tail”变量将保持不变。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多