Mã giả của Tarjan hoạt động như thế nào (được giải thích cho người quen thuộc với C hoặc Java)?


40

Câu chuyện ngắn

Một nhà khoa học máy tính nổi tiếng, Tarjan , đã viết một cuốn sách nhiều năm trước. Nó chứa mã giả hoàn toàn kỳ quái. Ai đó vui lòng giải thích nó?

Câu chuyện dài

Tarjan được biết đến với nhiều thành tựu, bao gồm cả việc anh ta là người khai thác cây splay . Ông đã xuất bản một cuốn sách " Cấu trúc dữ liệu và thuật toán mạng " trong những năm 1980.

Tất cả các mã giả trong cuốn sách của Tarjan được viết bằng ngôn ngữ của chính anh ấy. Các quy ước mã giả rất được yêu thích. Nó gần như là một ngôn ngữ thực sự, và người ta có thể tưởng tượng việc viết một trình biên dịch cho nó. Tarjan viết rằng ngôn ngữ của mình dựa trên ba điều sau đây:

Tôi hy vọng rằng ai đó quen thuộc với một hoặc hai ngôn ngữ trên, hoặc công việc của Tarjan, sẽ có thể trả lời câu hỏi của tôi.

Một ví dụ về hàm được viết bằng ngôn ngữ của Tarjan được hiển thị bên dưới:

heap function mesh (heap nodes h1, h2);

    if key(h1) > key(h2) → h1 ⟷  h2 fi;

    right (h1) := if right(h1) = null → h2

                  |right(h1) ≠ null → mesh (right(h1), h2) fi;

    if rank (left (h1)) < rank (right (h1)) → left(h1) ⟷ right(h1) fi;

rank (h1) := rank(right(h1)) + 1;

return h1,

end mesh;

Tôi đã thấy rất nhiều mã giả, nhưng tôi chưa bao giờ thấy bất cứ thứ gì như Tarjan. Mã giả của Tarjan hoạt động như thế nào? Làm thế nào các ví dụ về mã giả của Tarjan có thể được viết lại thành một cái gì đó trông giống C hoặc Java hơn? Nó thậm chí không cần là C hoặc Java. Cấu trúc if-other trong ngôn ngữ của Tarjan không chỉ khác với ngôn ngữ họ C, mà còn khác với Python, MATLAB và nhiều ngôn ngữ khác.


6
Điều gì, cụ thể, bạn không hiểu? Giải thích về cú pháp và ngữ nghĩa được đưa ra trong cuốn sách?
Raphael

8
Bạn đã sao chép mẫu từ một nơi nào đó hoặc tự sao chép nó? Là hai dòng cuối cùng bên trong cơ thể chức năng thực sự không thụt lề nhiều hơn? Và returntuyên bố có thực sự kết thúc bằng dấu phẩy không?
Bergi

Câu trả lời:


63

Mục lục

Tôi sẽ chia lời giải thích về mã giả của Tarjan thành các phần sau:

  1. Các khối If-other của Tarjan (các toán tử ->& |toán tử)
  2. Bài kiểm tra chuyển nhượng và bình đẳng ( :==)
  3. else if, nhưng không có elsecấu trúc
  4. Toán tử chuyển nhượng có điều kiện của Tarjan := if
  5. Ví dụ bổ sung của Tarjan if:= if
    5.5. Mảng Tarjan (hoặc Danh sách)

  6. Tóm tắt về các nhà khai thác

  7. Toán tử mũi tên nhọn của Tarjan ( )
  8. Các vòng lặp do Tarjan giống như các vòng lặp C / Java
  9. Toán tử gán điều kiện của Tarjan với tất cả các điều kiện sai

(1) Khối nếu khác của Tarjan

(các nhà khai thác |)

Cấu if-elsetrúc có lẽ là cấu trúc điều khiển cơ bản nhất trong ngôn ngữ của Tarjan. Ngoài các khối if giống như C, hành vi if-if gần như được tích hợp trong các bài tập của Tarjan và các vòng lặp của Tarjan. Toán tử mũi tên của Tarjan ->(hoặc →) là một dấu phân cách giữa điều kiện của câu lệnh if và khối thực thi của câu lệnh if.

Ví dụ: trong ngôn ngữ của Tarjan, chúng ta có thể có:

# Example One
if a = 4 → x := 9 fi    

Nếu chúng tôi dịch một phần dòng mã Tarjan ở trên sang C hoặc Java, chúng tôi sẽ nhận được như sau:

if (a = 4)
    x := 9
fi 

Thay vì một dấu ngoặc nhọn bên phải (như trong C và Java), Tarjan kết thúc một if-block bằng một cách viết ngược giống như ALGOL của từ khóa:fi

Nếu chúng tôi tiếp tục dịch ví dụ trên, chúng tôi nhận được:

if (a = 4) {
    x := 9
}

(2) Bài kiểm tra chuyển nhượng và bình đẳng ( :==)

Tarjan lấy các toán tử này từ ALGOL (sau này cũng thấy trong Pascal).

Tarjan sử dụng =cho các bài kiểm tra đẳng thức, không phải bài tập (vì vậy nó hoạt động như Java ==).

Để gán, Tarjan sử dụng :=, hoạt động như Java =.

Vì vậy, nếu chúng tôi tiếp tục dịch ví dụ của mình, chúng tôi có:

if (a == 4) {
    x = 9
}

Một thanh dọc (hoặc "ống" hoặc |) trong ngôn ngữ của Tarjan tương đương với else iftừ khóa trong C hoặc Java.
Ví dụ: trong ngôn ngữ của Tarjan, chúng ta có thể có:

# Example Two
if a = 4 → x := 9 |  a > 4  → y := 11 fi 

Mã Tarjan ở trên có nghĩa là:

if (a == 4) {
    x = 9
}
else if (a > 4)  {
    y = 11
}

(3) else ifchỉ và không có elsecấu trúc

Trước đó, tôi đã trình bày những điều cơ bản của if-statements mà không mô tả các sắc thái. Tuy nhiên, chúng tôi sẽ không thảo luận về một chi tiết nhỏ. Mệnh đề cuối cùng trong khối Tarjan-ian if-elsephải luôn chứa toán tử mũi tên ( ). Như vậy, không có elsengôn ngữ của Tarjan, chỉ else if. Điều gần nhất với một else-block trong ngôn ngữ của Tarjan là tạo điều kiện kiểm tra đúng nhất true.

if a = 4 → x := 9 |  a > 4  → y := 11 | true  → z := 99  fi

Trong C / Java, chúng ta sẽ có:

if (a == 4) {
    x = 9
}

else if (a > 4)  {
    y = 11
}
else { // else if (true)
    z = 99
} 

Các ví dụ dễ hiểu hơn các mô tả chung. Tuy nhiên, bây giờ chúng tôi có một số ví dụ trong vành đai của mình, hãy biết rằng hình thức chung của cấu trúc if-other của Tarjan như sau:

if condition
    → stuff to do
 | condition
    → stuff to do
 [...] 
 | condition 
    → stuff to do
fi       

Nhân vật |giống nhưif else

Nhân vật tách điều kiện kiểm tra ra khỏi công cụ cần làm.

(4) Toán tử chuyển nhượng có điều kiện của Tarjan := if

Tarjan ifcó thể được sử dụng theo hai cách rất khác nhau. Cho đến nay, chúng tôi chỉ mô tả một trong những cách sử dụng Tarjanian if. Hơi khó hiểu, Tarjan vẫn sử dụng ký hiệu / cú pháp ifcho loại if-construct thứ hai . Mà ifđang được sử dụng là dựa trên bối cảnh. Phân tích bối cảnh thực sự rất dễ thực hiện vì loại Tarjan thứ hai - ifluôn được cố định trước bởi một toán tử gán.

Ví dụ: chúng tôi có thể có mã Tarjan sau:

# Example Three
x := if a = 4 → 9 fi 

Bắt đầu tiêu hóa

Sau khi làm việc với mã Tarjan một lúc, bạn sẽ quen với thứ tự các thao tác. Nếu chúng ta ngoặc đơn điều kiện kiểm tra trong ví dụ trên, chúng ta có được:

x := if (a = 4) → 9 fi 

a = 4không phải là một hoạt động chuyển nhượng. a = 4giống như a == 4- nó trả về đúng hay sai.

Kết thúc tiêu hóa

Nó có thể giúp nghĩ về := ifcú pháp cho một toán tử đơn lẻ, khác với :=và trên ifthực tế, chúng ta sẽ gọi := iftoán tử là toán tử "gán điều kiện".

Đối với ifchúng tôi liệt kê (condition → action). Đối với := ifchúng tôi liệt kê (condition → value)đâu valuelà giá trị phía bên phải, chúng tôi có thể gán cho phía bên tráilhs

# Tarjan Example Four
lhs := if (a = 4) → rhs fi 

trong C hoặc Java có thể trông giống như:

# Example Four
if (a == 4) {
    lhs = rhs
}

Hãy xem xét ví dụ sau về "gán điều kiện" trong mã Tarjanian:

# Khởi tạo Tarjan của ví dụ Năm x: = a = 4 → 9 | a> 4 → 11 | đúng → 99 fi

Trong C / Java, chúng ta sẽ có:

// C/Java Instantiation of Example Five
if (a == 4) {
    x = 9
}
else if (a > 4)  {
    x = 11
}
else if (true) { // else
    x = 99
} 

(5) Tóm tắt về các nhà khai thác:

Cho đến nay, chúng ta có:

  • :=...... Toán tử gán (C / Java =)

  • =...... Kiểm tra tính bằng (C / Java ==)

  • ...... Phân cách giữa điều kiện kiểm tra của khối if và thân khối if

  • | ..... C / Java khác-nếu

  • if ... fi ..... khối if-other

  • := if... fi ..... Phân công có điều kiện dựa trên khối if-other

(5.5) Danh sách / Mảng Tarjan:

Ngôn ngữ của Tarjan có các thùng chứa giống như mảng. Cú pháp cho mảng Tarjan trực quan hơn nhiều so với ký hiệu cho các if elsecâu lệnh Tarjan .

list1  := ['lion', 'witch', 'wardrobe'];
list2a := [1, 2, 3, 4, 5];
list2b := [1, 2];
list3  := ["a", "b", "c", "d"];
list4  := [ ]; # an empty array

Phần tử mảng Tarjan được truy cập bằng dấu ngoặc đơn (), không phải dấu ngoặc vuông[]

Lập chỉ mục bắt đầu lúc 1. Như vậy

list3  := ["a", "b", "c", "d"]
# list3(1) == "a" returns true
# list3(2) == "b" return true 

Dưới đây cho thấy cách tạo một mảng mới chứa các phần tử thứ 1 và thứ 5 của [1, 2, 3, 4, 5, 6, 7]

nums := [1, 2, 3, 4, 5, 6, 7]
new_arr := [nums(1), nums(5)]

Toán tử đẳng thức được định nghĩa cho mảng. Các đoạn mã sau intrue

x := false
if [1, 2] = [1, 2, 3, 4, 5] --> x := true
print(x)

Cách của Tarjan để kiểm tra xem một mảng có trống hay không là so sánh nó với một mảng trống

arr := [1, 2]
print(arr = [ ])
# `=` is equality test, not assignment

Người ta có thể tạo một khung nhìn (không sao chép) của một mảng con, bằng cách cung cấp nhiều chỉ mục cho toán tử ()kết hợp với..

list3  := ["a", "b", "c", "d"]

beg    := list3(.. 2)
# beg == ["a", "b"]
# beg(1) == "a"

end    := list3(3..)
# end == ["c", "d"]
# end(1) == "c"

mid    := list3(2..3)
# mid == ["b", "c"]
# mid(2) == "c"

# `list3(4)` is valid, but `mid(4)` is not 

(6) Ví dụ bổ sung về Tarjan's if:= if

Sau đây là một ví dụ khác về phép gán điều kiện Tarjan ( := if):

# Tarjan Example Six
a  := (false --> a | true --> b | false --> c1 + c2 |  (2 + 3 < 99) --> d)  

(true --> b)(cond --> action)mệnh đề ngoài cùng bên trái có một điều kiện đúng. Do đó, phép gán ban đầu Ví dụ Six có hành vi gán giống nhưa := b

Dưới đây là ví dụ phức tạp nhất của chúng tôi về mã Tarjan cho đến nay:

# Tarjan Example -- merge two sorted lists

list function merge (list s, t);

return if s =[] --> t
        | t = [ ] --> s
        | s != [ ] and t != [] and s(l) <= t(1) -->
            [s(1)]& merge(s[2..], t)
        | s != [ ]and t != [ ] and s(1) > r(l) -->
            [t(1)] & merge (s,t(2..))
       fi
end merge;

Sau đây là bản dịch mã của Tarjan để hợp nhất hai danh sách được sắp xếp. Dưới đây không chính xác là C hoặc Java, nhưng nó gần với C / Java hơn nhiều so với phiên bản Tarjan.

list merge (list s, list t) { 

    if (s is empty) {
        return t;
    }
    else if (t is empty){
        return s;
    }
    else if  (s[1] <= t[1]) {
        return CONCATENATE([s[1]], merge(s[2...], t));
    else  { // else if (s[1] > t[1])
        return CONCATENATE ([t[1]], merge(s,t[2..]);
    }
}

Dưới đây là một ví dụ khác về mã Tarjan và bản dịch trong một cái gì đó tương tự như C hoặc Java:

heap function meld (heap h1, h2);

    return if h1 = null --> h2
            | h2 = null --> h1
            | h1 not null and h2 not null --> mesh (h1, h2) 
           fi
end meld;

Dưới đây là bản dịch C / Java:

HeapNode meld (HeapNode h1, HeapNode h2) {

    if (h1 == null) {
       return h2;
    }   
    else if (h2 == null) {
        return h1;
    } else {
        mesh(h1, h2)
    }
} // end function

(7) Toán tử mũi tên nhọn của Tarjan ( <-->)

Dưới đây là một ví dụ về mã Tarjan:

x <--> y    

Toán tử mũi tên kép ( ) làm gì trong ngôn ngữ của Tarjan?
Chà, hầu như tất cả các biến trong Ngôn ngữ của Tarjan đều là con trỏ. <-->là một hoạt động trao đổi. Các bản in sautrue

x_old := x
y_old := y
x <--> y
print(x == y_old) # prints true
print(y == x_old) # prints true

Sau khi thực hiện x <--> y, xtrỏ đến đối tượng yđược sử dụng để trỏ đến và ytrỏ đến đối tượng xđược sử dụng để trỏ đến.

Dưới đây là một tuyên bố Tarjan bằng cách sử dụng <-->toán tử:

x := [1, 2, 3]
y := [4, 5, 6]
x <--> y 

Dưới đây là bản dịch từ mã Tarjan ở trên sang mã giả thay thế:

Pointer X     = address of array [1, 2, 3];
Pointer Y     = address of array [4, 5, 6];
Pointer X_OLD = address of whatever X points to;
X = address of whatever Y points to;
Y = address of whatever X_OLD points to; 

Ngoài ra, chúng ta có thể có:

void operator_double_arrow(Array** lhs, Array** rhs) {

    // swap lhs and rhs

    int** old_lhs = 0;
    old_lhs = lhs;
    *lhs = *rhs;
    *rhs = *old_lhs;
    return;
}

int main() {

    Array* lhs = new Array<int>(1, 2, 3);
    Array* rhs = new Array<int>(4, 5, 6);
    operator_double_arrow(&lhs, &rhs);
    delete lhs;
    delete rhs;
    return 0;
} 

Dưới đây là một ví dụ về một trong các chức năng của Tarjan khi sử dụng toán tử:

heap function mesh (heap nodes h1, h2);
    if key(h1) > key(h2) → h1 ⟷  h2 fi;
    right (h1) := if right(h1) = null → h2
                   |right(h1) ≠ null → mesh (right(h1), h2)
                  fi;

    if rank (left (h1)) < rank (right (h1))
        → left(h1) ⟷ right(h1)
    fi;

    rank (h1) := rank(right(h1)) + 1;
    return h1;
end mesh;

Dưới đây là bản dịch meshchức năng của Tarjan sang mã giả không phải là C, nhưng trông giống C hơn (nói tương đối). Mục đích của việc này là để minh họa cách thức hoạt động của nhà điều hành Tarjan .

node pointer function mesh(node pointers h1, h2) {

    if (h1.key) > h2.key) {

         // swap h1 and h2
            node pointer temp;
            temp = h1;
            h1 = h2;
            h2 = temp;
    }

    // Now, h2.key <= h1.key   

    if (h1.right == null) {
        h1.right = h2;

    } else // h1.key != null {
        h1.right = mesh(h1.right, h2);
    }



    if (h1.left.rank < h1.right.rank ) {
        // swap h1.left and h1.right

        node pointer temp;
        temp = h1;
        h1 = h2;
        h2 = temp;
    }

    h1.rank = h1.right.rank + 1;
    return h1;
}    

(8) Các vòng lặp của Tarjan giống như các vòng lặp C / Java

Ngôn ngữ ifforcấu trúc của Tarjan quen thuộc với các lập trình viên C / Java. Tuy nhiên, từ khóa Tarjan cho một vòng lặp while là do. Tất cả các dotừ kết thúc bằng từ khóa od, đó là cách viết ngược do. Dưới đây là một ví dụ:

sum := 0
do  sum < 50 → sum := sum + 1 

Trong mã giả kiểu C, chúng ta có:

sum = 0;
while(sum < 50) {
    sum = sum + 1;
}

Trên đây thực sự không hoàn toàn đúng. Một vòng lặp do Tarjan thực sự là một C / Java while(true)với một khối if-if được lồng bên trong. Một bản dịch chính xác hơn của mã Tarjan như sau:

sum = 0;
while(true) {
    if (sum < 50) {
         sum = sum + 1;
         continue;
         // This `continue` statement is questionable
    }
    break;
}

Dưới đây, chúng ta có một Tarjan -loop phức tạp hơn do:

sum := 0
do  sum < 50 → sum := sum + 1 | sum < 99 → sum := sum + 5

Mã giả kiểu C / Java cho Tarjan -loop phức tạp donhư sau:

sum = 0;
while(true) {

    if (sum < 50) {
         sum = sum + 1;
         continue;
    }
    else if (sum < 99) {
         sum = sum + 5;
         continue;
    }
    break;
}

(9) Toán tử gán điều kiện của Tarjan với tất cả các điều kiện sai

Mặc dù lời giải thích dài dòng ở trên bao gồm hầu hết mọi thứ, một vài vấn đề vẫn chưa được giải quyết. Tôi hy vọng rằng một ngày nào đó người khác sẽ viết một câu trả lời được cải tiến mới dựa trên câu trả lời của tôi.

Đáng chú ý, khi toán tử gán điều kiện := ifđược sử dụng và không có điều kiện nào đúng, tôi không phải là giá trị nào được gán cho biến.

x  := if (False --> 1| False --> 2 | (99 < 2) --> 3) fi

Tôi không chắc chắn, nhưng có thể là không có nhiệm vụ nào được thực hiện để x:

x = 0;
if (false) {
     x = 1;
}
else if (false) {
     x = 2;
}
else if (99 < 2) {
     x = 3;
}
// At this point (x == 0)

Bạn có thể yêu cầu biến bên trái nhìn thấy trong := ifcâu lệnh phải được khai báo trước đó. Trong trường hợp đó, ngay cả khi tất cả các điều kiện là sai, biến vẫn sẽ có giá trị.

Ngoài ra, có lẽ tất cả các điều kiện sai thể hiện lỗi thời gian chạy. Một cách khác là trả về một nullgiá trị đặc biệt và lưu trữ nulltrong đối số bên trái của bài tập.


7
Tôi nghĩ rằng chỉ cần thực hiện một thông dịch viên / dịch giả và / hoặc viết một ngữ nghĩa hoạt động sẽ là một cách có giá trị hơn để sử dụng thời gian của bạn liên quan đến điều này.
Derek Elkins

2
Điều đáng chú ý là một số tính năng này "kỳ lạ" hơn các tính năng khác. Ví dụ, có thể có nhiều ngôn ngữ =có nghĩa là so sánh như ở đó nghĩa là gán (nếu tôi đã từng viết một ngôn ngữ, tôi sẽ biến nó thành một lỗi cú pháp, và chỉ có :===). Mặt khác, toán tử hoán đổi là loại điều chỉ xảy ra trong các ngôn ngữ chuyên ngành nơi nó là một hoạt động phổ biến; tuy nhiên, trong các ngôn ngữ khác, bạn chỉ có thể giả sử một chức năng thư viện được gọi swapvà thay thế h1 ⟷ h2bằng swap(h1, h2)thay vì viết ra việc thực hiện mỗi lần.
IMSoP

2
Tại sao lại [1, 2] = [1, 2, 3, 4, 5]đúng?
Erhannis

3
Người |điều khiển là một người bảo vệ . Chúng được sử dụng trong Haskell (và tôi tin rằng các ngôn ngữ chức năng khác) trong định nghĩa hàm: f x | x == 0 = 1; x == 1 = 1; otherwise = f (x-1) + f(x-2)đây otherwiselà bí danh cho Truefđịnh nghĩa các số Wikipedia.
Bakuriu

2
@DerekElkins Tại sao bạn nghĩ như vậy? So với việc đơn giản là viết lên sự hiểu biết của một người bằng ngôn ngữ tự nhiên (đến mức độ chi tiết đủ để người khác hiểu), cả hai hoạt động bạn đề cập sẽ mất nhiều thời gian hơn tôi có thể nói. Không rõ ràng rằng nó sẽ là một cách sử dụng thời gian có giá trị hơn (đặc biệt nếu mục tiêu đang tìm kiếm chủ yếu là sự hiểu biết ).
ShreevatsaR

7

Chưa bao giờ thấy điều này trước đây nhưng tôi nghĩ rằng tôi có thể suy ra ý nghĩa của ngữ cảnh .. Có lẽ đó phải là một hoạt động hoán đổi và if G1 -> S1 | G2 - >S2 | ... filà một cấu trúc kiểu if / then / other cũng trả về một giá trị, như toán ?:tử ternary trong C và Java.

Với điều đó, chúng ta có thể viết hàm trên bằng một ngôn ngữ giống như Java:

HeapNode mesh(HeapNode h1, HeapNode h2)
{
  if(h1.key > h2.key)
  {
    // swap h1 and h2

    HeapNode t = h1;
    h1 = h2;
    h2 = t;
  }

  // One of the two cases has to hold in this case so we won't get to the
  // exception, but it'd be an exception if none of the cases were satisified
  // since this needs to return *something*.

  h1.right = (h1.right == null) ? h2 
             : (h1.right != null) ? mesh(h1.right, h2) 
             : throw new Exception();

  if(h1.left.rank < h1.right.rank)
  {
    // swap h1.left and h1.right

    HeapNode t = h1.left;
    h1.left = h1.right;
    h1.right = t;
  }

  h1.rank = h1.right.rank + 1;

  return h1;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.