Algol 60
Aqui está um boolean procedure
que faz o que a pergunta pede (observação: o Algol 60 é definido em termos de uma lista de tokens sem fixar sintaxe para eles; o abaixo usa a sintaxe Marst para representar os tokens individuais que compõem o programa):
boolean procedure recursion detector(n);
boolean n;
begin
own boolean nested, seen nested;
boolean was nested, retval;
was nested := nested;
begin if nested then seen nested := true end;
nested := true;
retval := n; comment "for the side effects, we ignore the result";
nested := was nested;
retval := seen nested;
begin if ! nested then seen nested := false end;
recursion detector := retval
end;
Verificação
Aqui está o código de teste que eu usei:
procedure outboolean(c, b);
integer c;
boolean b;
begin
if b then outstring(c, "true\n") else outstring(c, "false\n")
end;
begin
outboolean(1, recursion detector(false));
outboolean(1, recursion detector(true));
outboolean(1, recursion detector(recursion detector(false)));
outboolean(1, recursion detector(false | recursion detector(true)));
outboolean(1, recursion detector(false & recursion detector(true)));
outboolean(1, recursion detector(recursion detector(recursion detector(false))))
end
Como esperado, a saída é:
false
false
true
true
true comment "because & does not short-circuit in Algol 60";
true
Explicação
O Algol 60 tem uma ordem de avaliação diferente da maioria dos idiomas, que possui uma lógica própria, e é realmente muito mais poderosa e geral do que a ordem de avaliação típica, mas é bastante difícil para os humanos entenderem (e também bastante difícil para computadores para implementar eficientemente, e foi por isso que foi alterado para o Algol 68). Isso permite uma solução sem nenhum tipo de trapaça (o programa não precisa olhar para a árvore de análise ou algo parecido e, ao contrário de quase todas as outras soluções aqui, isso funcionaria perfeitamente se a chamada aninhada fosse feita por meio de um FFI).
Também decidi mostrar algumas outras peculiaridades do idioma. (Notavelmente, nomes de variáveis podem conter espaços em branco; isso é bastante útil para facilitar a leitura, porque não podem conter sublinhados. Também adoro o fato de o indicador de comentário ser a palavra literal comment
na maioria das codificações de sintaxe. Algol 68 achou isso bastante estranho comentários e introduzidos ¢
como uma alternativa. Normalmente, as citações no corpo do comentário não são necessárias, apenas as adiciono para maior clareza e para impedir que o comentário termine acidentalmente quando eu digito um ponto-e-vírgula.) Na verdade, eu realmente gosto dos conceitos gerais do idioma (se não os detalhes), mas é tão detalhado que raramente uso no PPCG.
A principal maneira pela qual o Algol 60 difere das linguagens que ele inspirou (como o Algol 68 e indiretamente C, Java, etc; pessoas que conhecem o K&R C provavelmente reconhecerão essa sintaxe para funções) é que um argumento de função é tratado um pouco como uma pequena lambda própria; por exemplo, se você der o argumento 5
para uma função que é apenas o número 5, mas se você der o argumento, x+1
obterá exatamente o que especificou, o conceito de " x
mais 1", em vez do resultado de x
mais 1. A diferença aqui é que, se houver x
alterações, as tentativas de avaliar o argumento da função em questão verão o novo valor dex
. Se um argumento da função não for avaliado dentro da função, também não será avaliado fora da função; da mesma forma, se for avaliado várias vezes dentro da função, será avaliado separadamente a cada vez (assumindo que isso não pode ser otimizado). Isso significa que é possível fazer coisas como capturar a funcionalidade de, digamos, if
ou while
em uma função.
Neste programa, estamos explorando o fato de que, se uma chamada para uma função aparecer em um argumento para essa função, isso significa que a função será executada recursivamente (porque o argumento é avaliado exatamente no ponto ou pontos em que a função o avalia) , não antes ou depois, e isso deve necessariamente estar dentro do corpo da função). Isso reduz o problema de detectar se a função está sendo executada recursivamente, o que é muito mais fácil; tudo o que você precisa é de uma variável local do encadeamento que detecte se há uma chamada recursiva (além disso, nesse caso, outra para comunicar informações de outra maneira). Podemos usar uma variável estática (ou seja,own
) para esse fim, porque o Algol 60 é de thread único. Tudo o que precisamos fazer depois é colocar tudo de volta do jeito que estava, para que a função funcione corretamente se for chamada várias vezes (conforme exigido pelas regras do PPCG).
A função não retorna o valor desejado das chamadas internas no momento (pelo menos se você assumir que elas devem procurar chamadas próprias apenas em seus argumentos, em vez de se contar); tornar esse trabalho bastante fácil, usando os mesmos princípios gerais, mas mais complexo e obscureceria o funcionamento da função. Se for considerado necessário cumprir a pergunta, não deve ser muito difícil mudar.
print(func(), func(func()))
, ou apenas haverá uma chamada de nível superior para a função logo após sua definição?