Algol 60
Aqui está um boolean procedureque 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 commentna 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 5para uma função que é apenas o número 5, mas se você der o argumento, x+1obterá exatamente o que especificou, o conceito de " xmais 1", em vez do resultado de xmais 1. A diferença aqui é que, se houver xalteraçõ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, ifou whileem 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?