Як працює псевдокод Таряна (пояснюється комусь, знайомому з C або Java)?


40

Коротка історія

Відомий вчений-комп’ютер Тарджан написав книгу років тому. Він містить абсолютно химерний псевдокод. Хтось, будь ласка, пояснить це?

Довга історія

Тарджан відомий багатьма досягненнями, в тому числі тим, що він був співавтором схильних дерев . Він опублікував книгу " Структури даних та мережеві алгоритми " протягом 1980-х років.

Весь псевдокод у книзі Таряна написаний мовою, що його придумав. Конвенції псевдокоду дуже регульовані. Це майже справжня мова, і можна було б уявити, як написати компілятор для цього. Тарджан пише, що його мова заснована на наступних трьох:

Я сподіваюся, що хтось, знайомий з однією або двома з перерахованих вище мов, або твором Таряна, зможе відповісти на моє запитання.

Приклад функції, написаної мовою Таряна, наведено нижче:

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;

Я бачив багато псевдокодів, але ніколи не бачив нічого подібного у Таряна. Як працює псевдокод Таряна? Як можна приклади псевдокоду Таряна переписати як щось, що більше нагадує C або Java? Це навіть не повинно бути C або Java. Конструкція if-else в мові Tarjan не тільки відрізняється від мов сімейства C, але і відрізняється від Python, MATLAB та багатьох інших.


6
Що, конкретно, ви не розумієте? Яке пояснення синтаксису та семантики наведено в книзі?
Рафаель

8
Ви скопіювали зразок звідкись чи переписали його самостійно? Чи справді два останні рядки всередині функціонального тіла не відведені більше? І чи returnсправді вислів закінчується комою?
Бергі

Відповіді:


63

Зміст

Я розділю моє пояснення Tarján псевдокоді «S в наступних розділах:

  1. Блоки Tarjan's If-else ( оператори ->та |оператори)
  2. Тести призначення та рівності ( :=і =)
  3. Існує else if, але немає elseконструкції
  4. Оператор умовного призначення Тарджана := if
  5. Додаткові приклади Тарджана ifта:= if
    5.5. Tarjan масиви (або списки)

  6. Підсумок операторів

  7. Оператор зі стрілкою з двократкою Таряна ( )
  8. Do-петлі Tarjan схожі на C / Java while-loops
  9. Оператор умовного призначення Таряна з усіма помилковими умовами

(1) Блоки Таряна, якщо інакше

(оператори та |)

if-elseКонструкція є , мабуть, найбільш фундаментальною структурою управління на мові Tarján в. На додаток до C-подібних if-блоків, поведінка if-else дуже вбудована в завдання Тарджана та циклів у той час як Tarjan. Оператор стрілки Tarjan ->(або →) - це роздільник між умовою if-оператора і блоком виконання оператора if.

Наприклад, мовою Тарджана у нас може бути:

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

Якщо ми частково переведемо рядок коду Tarjan вище в C або Java, отримаємо наступне:

if (a = 4)
    x := 9
fi 

Замість правильних фігурних дужок (як у C та Java) ifТарджан закінчує -блок із зворотним написанням ключового слова, схожим на ALGOL:fi

Якщо ми продовжимо перекладати наш вище приклад, отримаємо:

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

(2) Тести призначення та рівності ( :=і =)

Тарджан приймає цих операторів від ALGOL (пізніше його також бачили у Паскалі).

Тарджан використовує =для тестів рівності, а не для присвоєнь (тому він працює як Java ==).

Для призначення Tarjan використовує :=, що працює як Java =.

Таким чином, якщо ми продовжуємо перекладати наш приклад, ми маємо:

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

Вертикальна смуга (або "труба" або |) мовою Тарджана еквівалентна else ifключовому слову в C або Java.
Наприклад, мовою Тарджана у нас може бути:

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

Код Тарджана вище означає:

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

(3) else ifтільки і без elseконструкції

Раніше я висвітлював основи if-статей, не описуючи нюанси. Однак ми не будемо обговорювати невелику деталь. Останній пункт if-elseблоку Tarjan-ian повинен завжди містити оператор стрілки ( ). Як такої, elseмови Тарджана немає лише else if. Найближчим до else-блока в мові Тарджана - це зробити правильний тестовий стан true.

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

У C / Java ми мали б:

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

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

Приклади легше зрозуміти, ніж загальні описи. Однак тепер, коли ми маємо кілька прикладів під своїм поясом, знайте, що загальна форма формату конструкції Тарджана якщо інакше така:

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

Характер |схожийif else

Символ відокремлює умову тесту від завдання, яке потрібно виконати.

(4) Оператор умовного призначення Таряна := if

Тарджана ifможна використовувати два дуже різні способи. Поки ми описали лише одне із застосувань Тар'яни if. Трохи заплутано, Тарджан все ще використовує позначення / синтаксис ifдля другого типу if-конструкції. Що ifвикористовується, базується на контексті. Аналіз контексту насправді зробити дуже просто, оскільки другий тип Тарджана if- завжди заздалегідь зафіксований оператором призначення.

Наприклад, у нас може бути такий код Таряна:

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

Почніть Digression

Працюючи з кодом Tarjan деякий час, ви звикаєте до порядку операцій. Якщо ми встановимо в дужках умову тесту в наведеному вище прикладі, отримаємо:

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

a = 4не є операцією призначення. a = 4це як a == 4- повертає правду чи хибність.

Кінцеве відступлення

Це може допомогти розглянути := ifяк синтаксис для одного оператора, відмінне від :=і ifнасправді, ми будемо називати := ifоператора як оператора "умовного призначення".

Бо ifми перераховуємо (condition → action). Оскільки := ifми перераховуємо, (condition → value)де valueце праворучне значення, яке ми можемо призначити лівомуlhs

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

на мові C або Java може виглядати так:

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

Розглянемо наступний приклад "умовного призначення" в Тар'янському коді:

# Тарджан Ігнорування прикладу п’ять x: = a = 4 → 9 | a> 4 → 11 | правда → 99 fi

У C / Java ми мали б:

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

(5) Підсумок операторів:

Поки що ми маємо:

  • :=...... Оператор призначення (C / Java =)

  • =...... Тест рівності (C / Java ==)

  • ...... Розмежувач між умовою тесту блоку if-блоком та тілом блоку if-блоку

  • | ..... C / Java else-if

  • if ... fi ..... if-else блок

  • := if... fi ..... Умовне призначення на основі блоку if-else

(5.5) Списки / масиви Tarjan:

Мова Tarjan має вбудовані контейнери, схожі на масив. Синтаксис масивів Tarjan набагато інтуїтивніше, ніж позначення для if elseтверджень Tarjan .

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

Елементи масиву Tarjan доступні в дужках (), а не в квадратних дужках[]

Індексація починається з 1. Таким чином,

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

Нижче показано, як створити новий масив, що містить 1-й та 5-й елементи [1, 2, 3, 4, 5, 6, 7]

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

Оператор рівності визначений для масивів. Наступний код друкуєтьсяtrue

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

Шлях Таряна перевірити, чи масив порожній - порівняти його з порожнім масивом

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

Можна створити перегляд (а не копіювати) підмасиву, надавши оператору кілька індексів у ()поєднанні з..

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) Додаткові приклади Тарджана ifта:= if

Далі наведено ще один приклад умовного призначення Таряна ( := if):

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

(true --> b)є крайнім лівим (cond --> action)пунктом, що має справжню умову. Таким чином, вихідний приклад Приклад Шість має таку саму поведінку призначення, як іa := b

Нижче наш найскладніший приклад коду Tarjan на даний момент:

# 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;

Далі - переклад коду Тарджана для об'єднання двох відсортованих списків. Далі не зовсім C або Java, але це набагато ближче до C / Java, ніж версія 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..]);
    }
}

Нижче ще один приклад коду Tarjan та перекладу чимось схожим на 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;

Нижче переклад 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) Оператор зі стрілкою з двократкою Таряна ( <-->)

Нижче наведено приклад коду Tarjan:

x <--> y    

Що робить оператор подвійної стрілки ( ) мовою Тарджана?
Ну, майже всі змінні в мові Таряна є покажчиками. <-->- це операція своп. Наступні відбиткиtrue

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

Після виконання x <--> y, xвказує на об'єкт , який yвикористовується для вказівки , і yвказує на об'єкт , який xвикористовується для вказівки.

Нижче наводиться оператор Tarjan за допомогою <-->оператора:

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

Нижче переклад з коду Тарджана вище на альтернативний псевдокод:

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; 

Як варіант, ми могли б мати:

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;
} 

Нижче наводиться приклад однієї з функцій Tarjan за допомогою оператора:

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;

Нижче переклад функції Таряна meshв псевдо-код, який не є C, але більше схожий на C (відносно кажучи). Мета цього - проілюструвати, як працює оператор 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) do-петлі Tarjan схожі на C / Java while-loops

Мова ifта forконструкції Таряна знайомі програмістам на C / Java. Однак ключове слово Tarjan для циклу while є do. Усі doконтури закінчуються ключовим словом od, яке є зворотним написанням do. Нижче наведено приклад:

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

У псевдокоді в стилі С ми маємо:

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

Сказане насправді не зовсім правильно. До-петля Tarjan - це справді C / Java while(true), якщо все-таки вкладений блок if-else. Більш буквальний переклад коду Таряна полягає в наступному:

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

Нижче у нас є складніший doTarjan -loop:

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

doПсевдокод у стилі C / Java для складної Tarjan -loop такий:

sum = 0;
while(true) {

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

(9) Оператор умовного призначення Таряна з усіма помилковими умовами

Хоча довге пояснення вище стосується більшості речей, деякі питання залишаються невирішеними. Я сподіваюся, що хтось хтось коли-небудь напише нову вдосконалену відповідь на основі моєї, яка відповідає на ці країни.

Зокрема, коли використовується оператор умовного призначення := if, і жодна умова не відповідає дійсності, я не є тим, яке значення присвоюється змінній.

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

Я не впевнений, але можливо, що жодне завдання не робиться x:

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

Ви можете вимагати := ifпопередньо оголосити ліву змінну, показану у виписці. У цьому випадку, навіть якщо всі умови помилкові, змінна все одно матиме значення.

Крім того, можливо, всі помилкові умови представляють помилку виконання. Інша альтернатива - повернути спеціальне nullзначення та зберегти nullв лівій аргументі завдання.


7
Я думаю, що просто впровадження перекладача / перекладача та / або написання оперативної семантики було б більш цінним способом використати свій час щодо цього.
Дерек Елкінс

2
Варто зазначити, що деякі з цих особливостей є "екзотичнішими", ніж інші. Наприклад, є, мабуть, стільки мов, де =означає порівняння, а де це означає присвоєння (якщо я коли-небудь писав мову, я би робив це синтаксичною помилкою, а просто маю :=і ==). З іншого боку, оператор swap - це те, що трапляється лише в спеціалізованих мовах, де це була звичайна операція; на інших мовах, хоча, ви могли б просто взяти на себе функцію бібліотеки під назвою swapі замінити h1 ⟷ h2з , swap(h1, h2)а не виписуючи про здійсненні кожен раз.
IMSoP

2
Чому це [1, 2] = [1, 2, 3, 4, 5]правда?
Ерханніс

3
|Оператор є охоронцем . Вони використовуються в Haskell (і я вважаю, що інші функціональні мови) у визначеннях функцій: f x | x == 0 = 1; x == 1 = 1; otherwise = f (x-1) + f(x-2)ось otherwiseпсевдонім для Trueта fвизначає номери полів.
Бакуріу

2
@DerekElkins Чому ти так вважаєш? На відміну від простого написання розуміння природною мовою (до рівня деталізації, достатнього для розуміння іншими людьми), обидві дії, які ви згадуєте, зайняли б значно більше часу, наскільки я можу сказати. Не зрозуміло, що було б більш цінним використання часу (особливо якщо мета, яка шукається, - це насамперед розуміння ).
ShreevatsaR

7

Ніколи раніше цього не бачив, але я думаю, що я можу зробити висновок про те, що мається на увазі з контексту. Імовірно, це повинна бути операція swap, і if G1 -> S1 | G2 - >S2 | ... fiце конструкція типу if / then / else, яка також повертає значення, як-от потрійний ?:оператор у C та 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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.