2014-12-08 2 views
0

비슷한 질문을 많이하는 것을 알고 있습니다. 나는 그것들을 읽었고, 아직도 내 코드가하는 일을 왜하는지 이해하지 못한다. 그것은 교육을 위해서만 사용되며, 간단한 LinkedList (단일 링크)를 구현했으며 두 개의 정렬 된 목록의 병합을 구현하려고했습니다. 여기에 다음과 같은 코드는 코드루비 참조 및 값 전달 by

#!/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) 

목록 1과리스트 2가 업데이트되지 않았기 때문에, 그것은 따라서, 무한 루프에서 실행되었다 루프는 항상 진행되고있었습니다. 목록 1 & list2 변수가 append 함수에서 다른 객체에 할당되었으므로 업데이트되지 않았습니다. 따라서 함수에서 복귀 할 때 list1과 list2는 원래 참조에서 "취소"되었습니다. 그러나 "꼬리"참조가 업데이트되었습니다! 코드를 실행하면 코드를 볼 수 있습니다. 꼬리는 항상 새 병합 된 목록의 "꼬리"이며 새 목록의 "머리"는 아닙니다. 나는 ... 꼬리도 APPEND에서 새로운 객체를 기준으로되어 있기 때문에 함수에서 반환 할 때, 그래서, "꼬리"여전히 "머리"되어야하는 이유

감사합니다 (롤이 미친) 이해 해달라고

답변

0

루비 문제는 당신에게 많은 것을 숨긴다는 것입니다. 객체는 참조로 보이는 것처럼 전달되지만 실제로 영리한 위장의 포인터입니다. 포인터를 "참조 해제"하면 참조와 달리 원본 데이터를 조작합니다. C++과 달리 명시 적으로 만들어졌지만 루비는 이것이 사실이라고 생각 나게하지 않습니다.

그래서이 메서드의 인수는 개체에 대한 포인터처럼 동작하는 변수입니다. 다른 값으로 변경해도 원본에는 영향을주지 않습니다. 메서드를 호출하거나 속성을 변경하면 원본에 영향을 미치므로 참조가 해제되었습니다.

구현에 많은 문제가 있습니다.

그래서 당신이 원형 목록을하지 않는
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 

, 그 일을해야 하나를 들어, append 같은 방법은 노드 클래스의 일부가되어야합니다. list 값은 수정되지 않으며, 단순히 통합되어 있습니다.

append 메서드는 개별 노드 (하나의 요소 목록)와 여러 노드로 구성된 긴 목록 모두에서 작동한다는 점에 유의하십시오. 일반적으로 프로그래밍 할 때는 예외적 인 경우를 피하기 위해 시도하고 코드를 최소화하여 제대로 작동하게 만듭니다.

+3

... 이것은 Java, C# 등에서와 같은 동작입니다. 일부 언어에서는 참조로 전달하려는 경우 (예 : 'ref String myVar'를 사용) _explicitly_ 할 수 있지만 기본적으로 값, 즉 그들을 조작하는 것이 아니라 호출자의 값을 변경하지 않는다는 것을 의미합니다. –

+0

정상적인 구현에서 알 수 있듯이 append는 Node 클래스에 속해야합니다. 그러나 그것은 내가 원한 것이 아니다;) 내가 원하는 것은 필자의 "꼬리"변수이다. 항상 새로운리스트의 머리를 가리킨다. 코드를 사용하려면 꼬리가 항상 목록의 TAIL을 가리 킵니다. 그리고 당신의 설명은 꼬리 varaible이 다른 객체를 가리키는 이유는 무엇입니까? (함수의 다른 객체를 가리키는 tail을 만들기 때문에 호출자는 차이를 보지 않아야합니다.) !!) – Nikkolasg

+0

로컬 변수를 변경해도 해당 블록 외부에서는 아무 효과가 없습니다. 영원히 유지 되려면 a) 전달 된 객체의 속성 (예 :'tail.next') 또는 b) 로컬 또는 클래스 수준의 @ @ 유형 인스턴스 변수 (예 : @ 꼬리)를 조작해야합니다.'꼬리 '를 재 할당하고 역 전파시키려는 시도는 효과가 없습니다. 대신, 여기에 제시 한 예제를 다시 시도하여 원하는 동작을 구현하십시오. – tadman