Como o pseudocódigo de Tarjan funciona (explicado a alguém familiarizado com C ou Java)?


40

A história curta

Um famoso cientista da computação, Tarjan , escreveu um livro anos atrás. Ele contém um pseudocódigo absolutamente bizarro. Alguém poderia explicar isso?

A longa história

Tarjan é conhecido por muitas realizações, incluindo o fato de que ele era o co-mentor das árvores espalhadas . Ele publicou um livro, " Estruturas de dados e algoritmos de rede ", durante os anos 80.

Todo o pseudocódigo do livro de Tarjan está escrito em uma linguagem de sua própria invenção. As convenções de pseudo-código são muito regimentadas. É quase uma linguagem verdadeira, e alguém poderia imaginar escrever um compilador para ela. Tarjan escreve que seu idioma é baseado nos três seguintes:

Espero que alguém familiarizado com um ou dois dos idiomas acima, ou o trabalho de Tarjan, possa responder à minha pergunta.

Um exemplo de uma função escrita na linguagem do Tarjan é mostrada abaixo:

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;

Eu já vi muitos pseudo-códigos, mas nunca vi nada como o de Tarjan. Como funciona o pseudocódigo de Tarjan? Como exemplos do pseudocódigo de Tarjan podem ser reescritos como algo que se parece mais com C ou Java? Nem precisa ser C ou Java. A construção if-else na linguagem Tarjan não é apenas diferente das linguagens da família C, mas também é diferente do Python, MATLAB e muitos outros.


6
O que, especificamente, você não entende? Que explicação de sintaxe e semântica é dada no livro?
Raphael

8
Você copiou a amostra de algum lugar ou a transcreve? As duas últimas linhas dentro do corpo da função realmente não são mais recuadas? E a returndeclaração realmente termina em vírgula?
Bergi 5/02

Respostas:


63

Índice

Dividirei minha explicação do pseudocódigo de Tarjan nas seguintes seções:

  1. Os blocos If-else de Tarjan (os operadores ->& |)
  2. Testes de atribuição e igualdade ( :=e =)
  3. Existe else if, mas nenhuma elseconstrução
  4. Operador de atribuição condicional de Tarjan := if
  5. Exemplos adicionais de Tarjan ife:= if
    5.5. Matrizes Tarjan (ou listas)

  6. Resumo dos Operadores

  7. Operador de flecha dupla de Tarjan ( )
  8. Os loops de Tarjan são como loops de C / Java
  9. Operador de atribuição condicional de Tarjan com todas as condições falsas

(1) Os blocos If-else de Tarjan

(os operadores e |)

O if-elseconstruto é talvez a estrutura de controle mais fundamental na linguagem de Tarjan. Além dos blocos if-like C, o comportamento if-else está quase embutido nas atribuições de Tarjan e nos loops while de Tarjan. O operador de seta do Tarjan ->(ou →) é um delimitador entre a condição de uma instrução if e o bloco de execução de uma instrução if.

Por exemplo, no idioma de Tarjan, podemos ter:

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

Se traduzirmos parcialmente a linha do código Tarjan acima em C ou Java, obteremos o seguinte:

if (a = 4)
    x := 9
fi 

Em vez de chaves entre chaves (como em C e Java), Tarjan encerra um if-block com uma ortografia semelhante à ALGOL da palavra-chave:fi

Se continuarmos traduzindo nosso exemplo acima, obtemos:

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

(2) Testes de atribuição e igualdade ( :=e =)

Tarjan pega esses operadores da ALGOL (posteriormente vistos também em Pascal).

O Tarjan usa =para testes de igualdade, não atribuições (portanto, funciona como Java ==).

Para atribuição, o Tarjan usa :=, que funciona como Java =.

Assim, se continuarmos traduzindo nosso exemplo, teremos:

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

Uma barra vertical (ou "pipe" ou |) na linguagem do Tarjan é equivalente à else ifpalavra - chave em C ou Java.
Por exemplo, no idioma de Tarjan, podemos ter:

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

O código Tarjan acima se traduz em:

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

(3) else ifúnico e nenhum elseconstruto

Anteriormente, eu cobri o básico das ifdeclarações sem descrever as nuances. No entanto, não discutiremos um pequeno detalhe. A última cláusula em um if-elsebloco Tarjan-ian deve sempre conter um operador arrow ( ). Como tal, não existe apenas elsena língua de Tarjan else if. A coisa mais próxima de um elsebloco na linguagem do Tarjan é fazer a condição de teste mais à direita true.

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

Em C / Java, teríamos:

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

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

Exemplos são mais fáceis de entender do que descrições gerais. No entanto, agora que temos alguns exemplos em mente, saiba que o formal geral da construção if-else de Tarjan é o seguinte:

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

O personagem |é comoif else

O caractere separa a condição de teste das coisas a fazer.

(4) Operador de atribuição condicional da Tarjan := if

O Tarjan ifpode ser usado de duas maneiras muito diferentes. Até agora, descrevemos apenas um dos usos do Tarjaniano if. Um tanto confuso, Tarjan ainda usa a notação / sintaxe ifpara o segundo tipo de ifconstrução. Qual ifestá sendo usado é baseado no contexto. Analisar o contexto é realmente muito fácil de fazer, pois o segundo tipo de Tarjan- ifé sempre pré-fixado por um operador de atribuição.

Por exemplo, podemos ter o seguinte código Tarjan:

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

Iniciar digressão

Depois de trabalhar com o código Tarjan por um tempo, você se acostuma à ordem das operações. Se colocarmos parênteses na condição de teste no exemplo acima, obteremos:

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

a = 4não é uma operação de atribuição. a = 4é como a == 4- retorna verdadeiro ou falso.

Digressão final

Pode ajudar a pensar := ifna sintaxe de um único operador, distinto :=e ifNa verdade, vamos nos referir ao := ifoperador como o operador de "atribuição condicional".

Para ifnós listamos (condition → action). Para := iflistamos (condition → value)onde valueé teh valor mão do lado direito podemos atribuir à mão do lado esquerdolhs

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

em C ou Java pode se parecer com:

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

Considere o seguinte exemplo de "atribuição condicional" no código tarjaniano:

Instanciação Tarjan do Exemplo Cinco x: = a = 4 → 9 | a> 4 → 11 | verdadeiro → 99 fi

Em C / Java, teríamos:

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

(5) Resumo dos operadores:

Até agora, temos:

  • :=...... Operador de atribuição (C / Java =)

  • =...... Teste de igualdade (C / Java ==)

  • ...... Delimitador entre a condição de teste de um bloco if e o corpo de um bloco if

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

  • if ... fi ..... bloco if-else

  • := if... fi ..... Atribuição condicional baseada em um bloco if-else

(5.5) Tarjan listas / matrizes:

O Tarjan's Language possui contêineres do tipo array embutidos. A sintaxe para matrizes Tarjan é muito mais intuitiva que a notação para if elseinstruções Tarjan .

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

Os elementos da matriz Tarjan são acessados ​​com parênteses (), não entre colchetes[]

A indexação começa em 1. Portanto,

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

Abaixo mostra como criar uma nova matriz contendo os 1º e 5º elementos de [1, 2, 3, 4, 5, 6, 7]

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

O operador de igualdade é definido para matrizes. O código a seguir é impressotrue

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

A maneira de Tarjan testar se uma matriz está vazia é compará-la com uma matriz vazia

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

Pode-se criar uma visão (não copiar) de uma sub-matriz, fornecendo múltiplos índices ao operador ()combinados com..

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) Exemplos adicionais de Tarjan ife:= if

A seguir, outros exemplos de uma atribuição condicional do Tarjan ( := if):

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

(true --> b)é a (cond --> action)cláusula mais à esquerda com uma condição verdadeira. Assim, o Exemplo Seis da atribuição original tem o mesmo comportamento de atribuição quea := b

Abaixo está o nosso exemplo mais complicado de código Tarjan até agora:

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

A seguir, uma tradução do código de Tarjan para mesclar duas listas classificadas. O seguinte não é exatamente C ou Java, mas é muito mais próximo do C / Java do que a versão 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..]);
    }
}

Abaixo está outro exemplo de código Tarjan e uma tradução em algo semelhante a C ou 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;

Abaixo está a tradução 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) Operador de flecha dupla de Tarjan ( <-->)

Abaixo está um exemplo de código Tarjan:

x <--> y    

O que um operador de seta dupla ( ) faz no idioma do Tarjan?
Bem, quase todas as variáveis ​​na linguagem do Tarjan são ponteiros. <-->é uma operação de troca. As seguintes impressõestrue

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

Após a execução x <--> y, xaponta para o objeto que ycostumava apontar e yaponta para o objeto que xcostumava apontar.

Abaixo está uma instrução Tarjan usando o <-->operador:

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

Abaixo está uma tradução do código Tarjan acima para o pseudocódigo 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; 

Como alternativa, poderíamos ter:

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

Abaixo está um exemplo de uma das funções do Tarjan usando o operador:

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;

Abaixo está uma tradução da meshfunção de Tarjan em pseudo-código que não é C, mas parece mais com C (relativamente falando). O objetivo disso é ilustrar como o operador do Tarjan funciona.

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) Os loops de Tarjan são como loops de C / Java

A linguagem ife as forconstruções de Tarjan são familiares para programadores de C / Java. No entanto, a palavra-chave Tarjan para um loop while é do. Todos os doloops terminam com a palavra-chave od, que é a grafia inversa de do. Abaixo está um exemplo:

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

No pseudocódigo no estilo C, temos:

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

O exposto acima não é exatamente correto. Um loop do Tarjan é realmente um C / Java while(true)com um bloco if-else aninhado dentro. Uma tradução mais literal do código Tarjan é a seguinte:

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

Abaixo, temos um Tarjan- doloop mais complicado :

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

O pseudocódigo no estilo C / Java para o complicado Tarjan- doloop é o seguinte:

sum = 0;
while(true) {

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

(9) Operador de atribuição condicional da Tarjan com todas as condições falsas

Embora a longa explicação acima cubra a maioria das coisas, algumas questões ainda não foram resolvidas. Espero que alguém mais algum dia escreva uma resposta nova e melhorada, com base na minha, que responda a esses dilemas.

Notavelmente, quando o operador de atribuição condicional := ifé usado e nenhuma condição é verdadeira, não sou o valor atribuído à variável.

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

Não tenho certeza, mas é possível que nenhuma atribuição seja feita para x:

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

Você pode exigir que a variável do lado esquerdo vista em uma := ifdeclaração seja declarada anteriormente. Nesse caso, mesmo que todas as condições sejam falsas, a variável ainda terá um valor.

Como alternativa, talvez todas as condições falsas representem um erro de tempo de execução. Outra alternativa é retornar um nullvalor especial e armazenar nullno argumento esquerdo da atribuição.


7
Penso que simplesmente implementar um intérprete / tradutor e / ou escrever uma semântica operacional teria sido uma maneira mais valiosa de usar seu tempo com relação a isso.
Derek Elkins

2
Vale a pena notar que alguns desses recursos são mais "exóticos" do que outros. Por exemplo, provavelmente existem tantos idiomas onde =significa comparação quanto onde significa atribuição (se eu escrevesse um idioma, eu o tornaria um erro de sintaxe, e apenas tenho :=e ==). Por outro lado, o operador de swap é o tipo de coisa que ocorreria apenas em idiomas especializados, onde era uma operação comum; em outros idiomas, você pode assumir uma função de biblioteca chamada swape substituir h1 ⟷ h2por, em swap(h1, h2)vez de escrever a implementação todas as vezes.
IMSoP 04/02

2
Porque é [1, 2] = [1, 2, 3, 4, 5]verdade?
Erhannis 04/02

3
O |operador é um guarda . Eles são usados ​​em Haskell (e acredito que outras linguagens funcionais) nas definições de funções: f x | x == 0 = 1; x == 1 = 1; otherwise = f (x-1) + f(x-2)aqui otherwiseestá um apelido para Truee fdefine os números de fibonacci.
Bakuriu

2
@DerekElkins Por que você acha isso? Comparadas a simplesmente escrever o entendimento em linguagem natural (com um nível de detalhe suficiente para ser entendido por outros seres humanos), as duas atividades mencionadas levariam muito mais tempo, até onde eu sei. Não está claro que seria um uso mais valioso do tempo (especialmente se o objetivo que está sendo procurado é principalmente o entendimento ).
ShreevatsaR

7

Nunca vi isso antes, mas acho que posso deduzir o que se entende por contexto. Presumivelmente, deve ser uma operação de troca e if G1 -> S1 | G2 - >S2 | ... fié uma construção do tipo if / then / else que também retorna um valor, como o ?:operador ternário em C e Java.

Com isso em mãos, poderíamos escrever a função acima em uma linguagem semelhante a 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;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.