Escriba una función (o macro) que devuelva verdadero si y solo si al menos uno de sus argumentos contiene una llamada a la función misma y falso en caso contrario .
Por ejemplo:
int a=function(); //a==false
int b=function(0); //b==false
int c=function(function(0)); //c==true
int d=function(3*function(1)+2); //d==true (weakly-typed language)
bool e=function(true||function(1)); //e==true (strongly-typed language)
EDITAR: La función / macro puede llamar a otras funciones / macros auxiliares.
EDIT 2: La función debe tomar al menos un argumento, a menos que el lenguaje utilizado se comporte como C, donde una función que no toma argumentos aún puede ser invocada con argumentos.
print(func(), func(func()))
, o solo habrá una llamada de alto nivel a la función justo después de que se haya definido?Respuestas:
Mathematica ...
... fue hecho para esto.
Todo es una expresión, y una expresión tiene una Cabeza y varios elementos. Así
1+2
es en realidadPlus[1,2]
, y{1,2}
es en realidadList[1,2]
. Esto significa que podemos hacer coincidir cualquier expresión para la cabeza que nos interesa, en este caso, la función enf
sí.Todo lo que necesitamos hacer es encontrar una manera de evitar que Mathematica evalúe los argumentos de la función antes de que se llame a la función, de modo que podamos analizar el árbol de expresión dentro de la función. Para qué es
Hold
yHoldAll
para qué sirve. Mathematica lo usa en todas partes para proporcionar casos especiales para ciertas implementaciones. Por ejemplo, si lo llamaLength[Permutations[list]]
, nunca creará todas esas permutaciones y desperdiciará un montón de memoria, sino que se dará cuenta de que simplemente puede calcular esto comoLength[list]!
.Veamos el código anterior en detalle, usando la última llamada
f[True || f[1]]
como ejemplo. Normalmente, Mathematica evaluará primero los argumentos de la función, por lo que esto simplemente provocaría un cortocircuito y una llamadaf[True]
. Sin embargo hemos establecidoEsto le indica a Mathematica que no evalúe los argumentos, por lo que
FullForm
la llamada (es decir, el árbol de expresión interna, sin ningún azúcar sintáctico) esY el argumento será realmente recibido de
f
esta forma. El siguiente problema es que, dentrof
, tan pronto como usemos el argumento, Mathematica intentará nuevamente evaluarlo. Podemos suprimir esto localmente conHold@x
(azúcar sintáctico paraHold[x]
). En este punto, tenemos un control sobre el árbol de expresión original y hacemos con él lo que queramos.Para buscar un patrón en un árbol de expresión podemos usar
FreeQ
. Comprueba que un patrón dado no se encuentra en el árbol de expresión. Usamos el patrón_f
, que coincide con cualquier subexpresión con headf
(exactamente lo que estamos buscando). Por supuesto,FreeQ
devuelve lo contrario de lo que queremos, por lo que negamos el resultado.Una sutileza más: he especificado el argumento como
x___
, que es una secuencia de 0 o más elementos. Esto asegura quef
funcione con cualquier número de argumentos. En la primera llamada,f[]
esto significa queHold@x
simplemente se convertiráHold[]
. Si hubiera múltiples argumentos, comof[0,f[1]]
, entoncesHold@x
seríaHold[0,f[1]]
.Eso es realmente todo lo que hay que hacer.
fuente
C ++ 11
Similar a las plantillas de expresión, podemos propagar el hecho de que llamamos a la función dentro de una lista de parámetros de funciones en su tipo de retorno.
Al principio necesitamos algunas clases y funciones auxiliares:
Ahora, podemos usarlo exactamente para el mismo código que en la pregunta:
Salida de ideone:
fuente
C#
Uso (en LINQPad ):
El truco aquí es hacer
f
más conscientes de los parámetros, pasando un tipo personalizado (PcgBool
) que es como un booleano.PD: Espero que devolver un tipo personalizado que se pueda convertir implícitamente en bool no se considere trampa. Técnicamente, puede usar el valor de retorno como si fuera del tipo bool, y la pregunta se hizo para "devolver verdadero si y solo si", etc., pero nunca dijo que el tipo de retorno debe ser bool.
fuente
f(new PcgBool())
?Lua
fuente
C ++ 11 TMP
Este es un poco más largo. Debido a algunas limitaciones en las plantillas de C ++, tuve que hacer todo con tipos. Así, "True" y los números se convirtieron en bool e int. También las operaciones +, - y || se convirtieron en add, mul y or_.
Espero que esto todavía califique como una respuesta válida.
fuente
is_given_foo
que se prefiera la segunda definición de la primera?C
No creo que se pueda hacer con procedimientos, pero siempre podemos (ab) usar macros.
Esto produce:
Nuestra macro
f
realiza un seguimiento de la profundidad actual y la profundidad máxima alcanzada desde que se invocó. Si el último es mayor que el primero, entonces se ha llamado recursivamente.fuente
&&
y||
. Puedo intentar canjear mi respuesta.C, 13
El argumento nunca se expande, por lo que no puede llamar a ninguna macro. No es más que un montón de texto sin formato. Por lo tanto, la respuesta siempre es falsa.
fuente
F(F(0))
felizmente evaluará el primer argumento de M,F(0)
. Ese argumento se expande a 0. Luego evalúa F sin pintura azul en su argumento de 0, dando como resultado 0. La restricción no recursiva no se aplica; es entonces cuando, por ejemplo, tengo#define F(X) G
y#define G F(Y)
está en juego; en este caso, al expandir F (0) a G y luego a F (Y),F
aparece el token . Como actualmente estoy expandiendo F, F tiene pintura azul en este caso y, por lo tanto, la expansión se detiene en F (Y).X
no está en la lista de reemplazo de argumentos, las macros vinculadas a ella nunca se expanden. Si interpretamos eso como una función que se llama, eso significa que las funciones del argumento nunca se llaman. Sí, creo que eso es correcto.C
Podemos contar la profundidad de recursión en la macro. Luego sobrescribimos el valor de retorno de la macro externa en la macro interna.
!!b
es normalizar el valor de retorno a un valor booleano. El código preprocesado termina así:fuente
printf("%d\n", foo(printf("%d\n", foo(1))))
. La llamada interna afoo
devuelve 1, pero no llamafoo
.C
La macro compara si su argumento comienza con "BLAH (".
fuente
BLAH(blub(BLAH(0)))
.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):Verificación
Aquí está el código de prueba que utilicé:
Como se esperaba, el resultado es:
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 argumentox+1
obtienes exactamente lo que especificaste, el concepto de "x
más 1", en lugar del resultado dex
más 1. La diferencia aquí es que six
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
owhile
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 decir
own
) 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.
fuente
Java
se
reset()
puede contar como auxiliar?Salida:
EDITAR
Aquí hay otra versión que no usa el
reset()
método, pero mucha locura. Crea y compila en tiempo de ejecución el código anterior con la llamada a la función pasada como argumento en stdin. Quería una solución más elegante, pero lamentablemente no tengo mucho tiempo para esto :(Para ejecutarlo simplemente llame por ejemplo
javac FuncOFunc.java function(function(1),function(),4)
.fuente
reset()
después de cadafunction
invocación. La idea de las funciones auxiliares es permitirle invocar otros métodos privados desde el interiorfunction
del cuerpo ... Pero esa es solo mi interpretación de la pregunta, dejemos que el autor de la decisión decida.reset()
versión menos. Aún así, el código anterior funciona si solo hay una llamada en el principal (sin el recuento variable y la función de reinicio)Pitón
Guárdelo como
selfarg.py
y ejecútelo ofrom selfarg import function
en otro script. No funciona en la respuesta.El uso de los marcos de pila actuales y externos
function
obtiene su nombre y lugar de llamada (número de archivo y línea). Luego abre el archivo obtenido y lo analiza en un árbol de sintaxis abstracta. Salta a la llamada de función identificada por el número de línea e inspecciona si tiene otra llamada de función con el mismo nombre en sus argumentos.editar : también está bien con python2. Cambió python3 a python en el título.
fuente