Come funziona lo pseudocodice di Tarjan (spiegato a qualcuno che ha familiarità con C o Java)?


40

La breve storia

Un famoso scienziato informatico, Tarjan , ha scritto un libro anni fa. Contiene uno pseudocodice assolutamente bizzarro. Qualcuno potrebbe spiegarlo per favore?

La lunga storia

Tarjan è noto per molti traguardi, incluso il fatto che era il coinventore degli alberi splay . Ha pubblicato un libro, " Strutture di dati e algoritmi di rete ", negli anni '80.

Tutto lo pseudo-codice nel libro di Tarjan è scritto in una lingua da lui ideata. Le convenzioni di pseudo-codice sono molto regolate. È quasi un vero linguaggio, e si potrebbe immaginare di scrivere un compilatore per questo. Tarjan scrive che la sua lingua si basa sui seguenti tre:

Spero che qualcuno che abbia familiarità con una o due delle lingue di cui sopra, o il lavoro di Tarjan, sarà in grado di rispondere alla mia domanda.

Di seguito è riportato un esempio di una funzione scritta nella lingua di 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;

Ho visto molti pseudo-codici, ma non ho mai visto nulla di simile a Tarjan. Come funziona lo pseudocodice di Tarjan? Come si possono riscrivere esempi dello pseudocodice di Tarjan come qualcosa che assomiglia di più a C o Java? Non deve nemmeno essere C o Java. Il costrutto if-else nella lingua di Tarjan non è solo diverso dalle lingue della famiglia C, ma anche diverso da Python, MATLAB e molti altri.


6
Cosa, in particolare, non capisci? Quale spiegazione della sintassi e della semantica è data nel libro?
Raffaello

8
Hai copiato il campione da qualche parte o lo hai trascritto da solo? Le ultime due righe all'interno del corpo della funzione non sono davvero più rientrate? E la returnfrase finisce davvero con una virgola?
Bergi,

Risposte:


63

Sommario

Dividerò la mia spiegazione dello pseudocodice di Tarjan nelle seguenti sezioni:

  1. Tarjan's If-else Blocks (the ->& |operatori)
  2. Test di assegnazione e uguaglianza ( :=e =)
  3. C'è else if, ma nessun elsecostrutto
  4. Operatore di assegnazione condizionale di Tarjan := if
  5. Ulteriori esempi di Tarjan ife:= if
    5.5. Tarjan Arrays (o Elenchi)

  6. Riepilogo degli operatori

  7. Operatore di freccia a doppia punta di Tarjan ( )
  8. I do-loop di Tarjan sono come C-Java while-loop
  9. Operatore di assegnazione condizionale di Tarjan con tutte le false condizioni

(1) Blocchi If-else di Tarjan

(gli operatori e |)

Il if-elsecostrutto è forse la struttura di controllo più fondamentale nella lingua di Tarjan. Oltre agli if-block di tipo C, il comportamento if-else è quasi integrato nei compiti di Tarjan e nei cicli while di Tarjan. L'operatore freccia di Tarjan ->(o →) è un delimitatore tra la condizione di un'istruzione if e il blocco di esecuzione di un'istruzione if.

Ad esempio, nella lingua di Tarjan potremmo avere:

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

Se traduciamo parzialmente la riga del codice Tarjan sopra in C o Java, otteniamo quanto segue:

if (a = 4)
    x := 9
fi 

Invece di una parentesi graffa a destra (come in C e Java) Tarjan termina un ifblocco con una ortografia all'indietro simile a ALGOL della parola chiave:fi

Se continuiamo a tradurre il nostro esempio sopra, otteniamo:

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

(2) Test di assegnazione e uguaglianza ( :=e =)

Tarjan prende questi operatori da ALGOL (in seguito visto anche in Pascal).

Tarjan utilizza =per i test di uguaglianza, non per le assegnazioni (quindi funziona come Java ==).

Per l'assegnazione, Tarjan utilizza :=, che funziona come Java =.

Pertanto, se continuiamo a tradurre il nostro esempio, abbiamo:

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

Una barra verticale (o "pipe" o |) nella lingua di Tarjan è equivalente alla else ifparola chiave in C o Java.
Ad esempio, nella lingua di Tarjan potremmo avere:

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

Il codice Tarjan sopra si traduce in:

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

(3) else ifsolo e nessun elsecostrutto

In precedenza, ho trattato le basi delle ifdichiarazioni senza descriverne le sfumature. Tuttavia, non discuteremo un piccolo dettaglio. L'ultima clausola in un if-elseblocco Tarjan-ian deve sempre contenere un operatore arrow ( ). Come tale, non esiste solo elsenella lingua di Tarjan else if. La cosa più vicina a un elseblocco nella lingua di Tarjan è rendere la condizione di test più giusta true.

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

In C / Java avremmo:

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

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

Gli esempi sono più facili da comprendere delle descrizioni generali. Tuttavia, ora che abbiamo alcuni esempi al nostro attivo, sappi che il formale generale del costrutto if-else di Tarjan è il seguente:

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

Il personaggio |è comeif else

Il personaggio separa le condizioni del test dalle cose da fare.

(4) Operatore di assegnazione condizionale di Tarjan := if

Tarjan ifpuò essere usato in due modi molto diversi. Finora abbiamo descritto solo uno degli usi del tarjaniano if. Un po 'confusamente, Tarjan usa ancora la notazione / sintassi ifper il secondo tipo di if-costruct. Quello che ifviene utilizzato si basa sul contesto. L'analisi del contesto è in realtà molto facile da fare poiché il secondo tipo di Tarjan ifè sempre pre-fissato da un operatore di assegnazione.

Ad esempio, potremmo avere il seguente codice Tarjan:

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

Inizia la digressione

Dopo aver lavorato con il codice Tarjan per un po ', ti abitui all'ordine delle operazioni. Se tra parentesi si verificano le condizioni del test nell'esempio sopra, si ottiene:

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

a = 4non è un'operazione di assegnazione. a = 4è come a == 4- restituisce vero o falso.

Fine Digressione

Può aiutare a pensare := ifcome sintassi per un singolo operatore, distinta da :=e ifIn effetti, ci riferiremo := ifall'operatore come operatore di "assegnazione condizionale".

Per ifnoi elenco (condition → action). Perché := ifelenchiamo (condition → value)dove si valuetrova il valore sul lato destro che potremmo assegnare al lato sinistrolhs

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

in C o Java potrebbe apparire come:

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

Si consideri il seguente esempio di "assegnazione condizionale" nel codice tarjaniano:

# Tarjan Istantanea dell'esempio Cinque x: = a = 4 → 9 | a> 4 → 11 | vero → 99 fi

In C / Java avremmo:

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

(5) Riepilogo degli operatori:

Finora abbiamo:

  • :=...... Operatore di assegnazione (C / Java =)

  • =...... Test di uguaglianza (C / Java ==)

  • ...... Delimitatore tra la condizione di test di un if-block e il corpo di un if-block

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

  • if ... fi ..... blocco if-else

  • := if... fi ..... Assegnazione condizionale basata su un blocco if-else

(5.5) Elenchi / matrici di Tarjan:

Tarjan's Language ha contenitori simili a array incorporati. La sintassi per le matrici di Tarjan è molto più intuitiva della notazione per le if elsedichiarazioni di Tarjan .

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

L'arraya di tarjan elementa è accessibile con parentesi (), non parentesi quadre[]

L'indicizzazione inizia alle 1. Così,

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

Di seguito mostra come creare un nuovo array contenente il 1o e il 5o elemento di [1, 2, 3, 4, 5, 6, 7]

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

L'operatore di uguaglianza è definito per le matrici. Viene stampato il seguente codicetrue

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

Il modo di Tarjan per verificare se un array è vuoto è confrontarlo con un array vuoto

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

Si può creare una vista (non copia) di un sotto-array, fornendo più indici agli operatori ()combinati con..

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) Esempi aggiuntivi di Tarjan ife:= if

Di seguito sono riportati altri esempi di assegnazione condizionale di Tarjan ( := if):

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

(true --> b)è la (cond --> action)clausola più a sinistra con una condizione vera. Pertanto, l'esempio di assegnazione originale Esempio 6 ha lo stesso comportamento di assegnazione dia := b

Di seguito è riportato il nostro esempio più complicato di codice Tarjan finora:

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

La seguente è una traduzione del codice di Tarjan per l'unione di due elenchi ordinati. Quanto segue non è esattamente C o Java, ma è molto più vicino a C / Java rispetto alla versione 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..]);
    }
}

Di seguito è riportato un altro esempio di codice Tarjan e una traduzione in qualcosa di simile a C o 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;

Di seguito è la traduzione 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) Operatore di freccia a doppia punta di Tarjan ( <-->)

Di seguito è riportato un esempio di codice Tarjan:

x <--> y    

Cosa fa un operatore Double Arrow ( ) nella lingua di Tarjan?
Bene, quasi tutte le variabili nella lingua di Tarjan sono puntatori. <-->è un'operazione di scambio. Le seguenti stampetrue

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

Dopo l'esecuzione x <--> y, xpunta sull'oggetto che era ysolito puntare e ypunta sull'oggetto che era xsolito puntare.

Di seguito è riportata un'istruzione Tarjan che utilizza l' <-->operatore:

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

Di seguito una traduzione dal codice Tarjan sopra allo pseudocodice alternativo:

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; 

In alternativa, potremmo avere:

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

Di seguito è riportato un esempio di una delle funzioni di Tarjan che utilizza l' operatore:

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;

Di seguito una traduzione della meshfunzione di Tarjan in pseudo-codice che non è C, ma assomiglia di più a C (relativamente parlando). Lo scopo è quello di illustrare come funziona l' operatore di 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) I do-loop di Tarjan sono come C-Java while-loop

Il linguaggio ife i forcostrutti di Tarjan sono familiari per i programmatori C / Java. Tuttavia, la parola chiave Tarjan per un ciclo while è do. Tutti i doloop terminano con la parola chiave od, che è l'ortografia all'indietro di do. Di seguito è riportato un esempio:

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

Nello pseudocodice in stile C, abbiamo:

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

Quanto sopra in realtà non è del tutto corretto. Un do-loop di Tarjan è in realtà un C / Java while(true)con un blocco if-else nidificato all'interno. Una traduzione più letterale del codice Tarjan è la seguente:

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

Di seguito, abbiamo un Tarjan do-loop più complicato :

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

Lo pseudocodice in stile C / Java per il complicato Tarjan do-loop è il seguente:

sum = 0;
while(true) {

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

(9) Operatore di assegnazione condizionale di Tarjan con tutte le false condizioni

Sebbene la lunga spiegazione di cui sopra copra la maggior parte delle cose, alcune questioni rimangono ancora irrisolte. Spero che qualcun altro un giorno scriverà una risposta migliorata basata sulla mia che risponde a questi dubbi.

In particolare, quando := ifviene utilizzato l' operatore di assegnazione condizionale e nessuna condizione è vera, non sono quale valore viene assegnato alla variabile.

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

Non sono sicuro, ma è possibile che non venga assegnato alcun compito a x:

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

Potresti richiedere che la variabile di sinistra sia vista in un := if un'istruzione sia precedentemente dichiarata. In tal caso, anche se tutte le condizioni sono false, la variabile avrà comunque un valore.

In alternativa, forse tutte le condizioni false rappresentano un errore di runtime. Un'altra alternativa è quella di restituire un nullvalore speciale e archiviarlo nullnell'argomento sinistro dell'assegnazione.


7
Penso che implementare semplicemente un interprete / traduttore e / o scrivere una semantica operativa sarebbe stato un modo più prezioso di usare il tuo tempo a riguardo.
Derek Elkins,

2
Vale la pena notare che alcune di queste funzionalità sono più "esotiche" di altre. Ad esempio, ci sono probabilmente molte lingue in cui =significa confronto rispetto a dove significa assegnazione (se mai scrissi una lingua, la renderei un errore di sintassi, e ho :=e ==). D'altra parte, l'operatore di swap è il tipo di cosa che si verificherebbe solo in lingue specializzate in cui era un'operazione comune; in altre lingue, però, si può solo assumere una funzione di libreria chiamata swape sostituirlo h1 ⟷ h2con swap(h1, h2)piuttosto che scrivere la realizzazione ogni volta.
IMSoP

2
Perché è [1, 2] = [1, 2, 3, 4, 5]vero?
Erhannis,

3
L' |operatore è una guardia . Sono usati in Haskell (e credo altri linguaggi funzionali) nelle definizioni delle funzioni: f x | x == 0 = 1; x == 1 = 1; otherwise = f (x-1) + f(x-2)ecco otherwiseun alias per Truee fdefinisce i numeri di fibonacci.
Bakuriu,

2
@DerekElkins Perché la pensi così? Rispetto al semplice scrivere la propria comprensione nel linguaggio naturale (a un livello di dettaglio sufficiente per essere compreso da altri umani), entrambe le attività che menzionate richiederebbero molto più tempo per quanto posso dire. Non è chiaro che sarebbe un uso più prezioso del tempo (soprattutto se l'obiettivo perseguito è principalmente la comprensione ).
ShreevatsaR,

7

Non l'ho mai visto prima, ma penso di poter dedurre cosa si intende dal contesto. Presumibilmente, deve essere un'operazione di scambio ed if G1 -> S1 | G2 - >S2 | ... fiè un costrutto if / then / else-type che restituisce anche un valore, come l' ?:operatore ternario in C e Java.

Con quello in mano potremmo scrivere la funzione sopra in un linguaggio simile a Java in questo modo:

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.