Algol 60
Aquí hay una boolean procedure
que hace lo que pide la pregunta (nota: Algol 60 se define en términos de una lista de tokens sin corregir la sintaxis para ellos, el siguiente usa la sintaxis de Marst para representar los tokens individuales que componen el 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;
Verificación
Aquí está el código de prueba que utilicé:
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 se esperaba, el resultado es:
false
false
true
true
true comment "because & does not short-circuit in Algol 60";
true
Explicación
Algol 60 tiene un orden de evaluación diferente al de la mayoría de los idiomas, que tiene una lógica propia, y en realidad es mucho más poderoso y general que el orden de evaluación típico, pero es bastante difícil de entender para los humanos (y también bastante difícil de entender). computadoras para implementar de manera eficiente, por lo que se cambió para Algol 68). Esto permite una solución sin ningún tipo de trampa (el programa no necesita mirar el árbol de análisis ni nada de eso, y a diferencia de casi todas las otras soluciones aquí, esto funcionaría bien si la llamada anidada se realizara a través de un FFI).
También decidí mostrar algunas otras peculiaridades del lenguaje. (Notablemente, los nombres de variables pueden contener espacios en blanco; esto es bastante útil para facilitar la lectura, ya que no pueden contener guiones bajos. También me encanta el hecho de que el indicador de comentario es la palabra literal comment
en la mayoría de las codificaciones de sintaxis. Algol 68 encontró esto bastante incómodo para abreviar) comentarios e introducidos ¢
como una alternativa. Las citas alrededor del cuerpo del comentario normalmente no son necesarias, solo las agrego para mayor claridad y para evitar que el comentario finalice accidentalmente cuando escribo un punto y coma.) Realmente me gustan los conceptos generales del lenguaje (si no los detalles), pero es tan detallado que rara vez lo uso en PPCG.
La forma principal en que Algol 60 difiere de los lenguajes que inspiró (como Algol 68 e indirectamente C, Java, etc.; las personas que conocen K&R C probablemente reconocerán esta sintaxis para las funciones) es que un argumento de función se trata un poco como una pequeña lambda propia; por ejemplo, si le das el argumento 5
a una función que es solo el número 5, pero si le das el argumento x+1
obtienes exactamente lo que especificaste, el concepto de " x
más 1", en lugar del resultado de x
más 1. La diferencia aquí es que si x
cambia, entonces los intentos de evaluar el argumento de la función en cuestión verán el nuevo valor dex
. Si un argumento de función no se evalúa dentro de la función, tampoco se evaluará fuera de la función; Del mismo modo, si se evalúa varias veces dentro de la función, se evaluará por separado cada vez (suponiendo que esto no se pueda optimizar). Esto significa que es posible hacer cosas como capturar la funcionalidad de, por ejemplo, if
o while
en una función.
En este programa, estamos explotando el hecho de que si una llamada a una función aparece en un argumento de esa función, eso significa que la función se ejecutará de forma recursiva (porque el argumento se evalúa exactamente en el punto o puntos en los que la función lo evalúa). , no antes ni después, y esto necesariamente debe estar dentro del cuerpo de la función). Esto reduce el problema de detectar si la función se ejecuta de forma recursiva, lo cual es mucho más fácil; todo lo que necesita es una variable local de subprocesos que detecte si hay una llamada recursiva (más, en este caso, otra para comunicar información de otra manera). Podemos usar una variable estática (es decirown
) para este propósito, porque Algol 60 es de un solo subproceso. Todo lo que tenemos que hacer después de eso es volver a poner todo como estaba, para que la función funcione correctamente si se llama varias veces (como lo requieren las reglas PPCG).
La función no devuelve el valor deseado de las llamadas internas en este momento (al menos si asume que deberían buscar auto-llamadas solo en sus argumentos, en lugar de contarlas); hacer que el trabajo sea bastante fácil usando los mismos principios generales, pero más complejo y oscurecería el funcionamiento de la función. Si se considera necesario cumplir con la pregunta, no debería ser demasiado difícil de cambiar.
print(func(), func(func()))
, o solo habrá una llamada de alto nivel a la función justo después de que se haya definido?