Bucle sin 'bucle' [cerrado]

85

Una pregunta similar a esta se hizo hace un par de años , pero esta es aún más complicada.

El desafío es simple. Escribir un programa (en el idioma de su elección) que ejecuta repetidamente el código sin necesidad de utilizar ninguna estructura de repetición, como while, for, do while, foreacho goto( Así que por todo lo que nitpickers, no se puede utilizar un bucle ). Sin embargo, la recursión no está permitida, en la función que se llama a sí misma sentido (véase la definición a continuación) . Eso haría este desafío demasiado fácil.

No hay restricciones sobre lo que debe ejecutarse en el ciclo, pero publique una explicación con su respuesta para que otros puedan entender exactamente lo que se está implementando.

Para aquellos que pueden estar colgados en las definiciones, la definición de un bucle para esta pregunta es:

A programming language statement which allows code to be repeatedly executed.

Y la definición de recursión para esta pregunta será su definición de función recursiva estándar:

A function that calls itself.

El ganador será la respuesta que tenga más votos positivos el 16 de julio a las 10 a.m., hora del este. ¡Buena suerte!

ACTUALIZAR:

Para calmar la confusión que aún se expresa, esto puede ayudar:

Reglas como se indicó anteriormente:

  • No uses bucles o goto
  • Las funciones no pueden llamarse a sí mismas
  • Haz lo que quieras en el 'bucle'

Si desea implementar algo y las reglas no lo rechazan explícitamente, continúe y hágalo. Muchas respuestas ya han torcido las reglas.

CailinP
fuente
27
Para aquellos que quieren un truco fácil, no me molesto en publicarlo: P Simplemente haga 2 funciones, function Allamadas function By function Bllamadas function Amientras 1 de las funciones realiza algo. Como la función no se llama a sí misma, debería ser válida según los criterios ^. ^
Teun Pronk
2
"Cambió al concurso de popularidad para centrarse en la creatividad" ¡Cambiar la pregunta es hacer trampa!
CousinCocaine
44
La definición de "recursión" no es muy útil. Sería mejor no permitir funciones recursivas , que son funciones que se refieren a sí mismas, directa o indirectamente.
lrn
3
Lo que no está claro son las "definiciones" de constructor de bucles y recursividad. Tampoco son muy precisos. Ejemplo: rep(f){f();f();}esta es una declaración (una declaración de función es una declaración en algunos idiomas) que permite ejecutar código repetidamente. ¿Está prohibido? Pide código para implementar un bucle. Si ese código es sintácticamente una declaración, simplemente lo ha rechazado. Otro ejemplo: f(b) { b(); g(b); }; g(b) { f(b); }. Yo diría que fes una función recursiva (al ser recursiva mutuamente con g). ¿Está prohibido?
lrn
3
@CailinP, de lo que estoy " colgado " es de que las preguntas en el sitio deberían estar en el tema del sitio: eso significa tener una especificación clara y objetiva, lo que esta pregunta no tiene.
Peter Taylor

Respuestas:

258

Rubí

def method_missing(meth,*args)
  puts 'Banana'
  send(meth.next)
end

def also
  puts "Orange you glad I didn't say banana?"
end

ahem

Manifestación

Se aclara la garganta, imprime "Plátano" 3070 veces y también pone "Naranja, ¿te alegra que no haya dicho plátano?".

Utiliza la ridícula funcionalidad de definición de método justo a tiempo de Ruby para definir cada método que se encuentra alfabéticamente entre las palabras 'ahem' y 'también' ("ahem", "ahen", "aheo", "ahep", "aheq", "aher", "ahes", "ahet", "aheu", "ahev" ...) para imprimir primero Banana y luego llamar al siguiente en la lista.

histocrat
fuente
44
Eventualmente toca "también", que está definido y, por lo tanto, no falta.
histocrat
77
Esto es histérico.
Michael B
44
@barrycarter: en Ruby, String#nextque se llama en method_missingfunciones más o menos como agregar 1 a un número, excepto que funciona con todos los caracteres alfanuméricos (y no alnums si son los únicos caracteres en la cadena). Ver ruby-doc.org/core-2.1.2/String.html#method-i-next
3Doubloons
2
@NickT es utilizable en clases como constructores XML donde puede crear cualquier etiqueta creada solo por b.my_tag. También se usa en modelos ActiveRecord o OpenStruct. En 'Wat talk', dice que global method_missinges malo, pero el alcance es impresionante.
Hauleth
2
Recuerdo un viejo comentario sobre otro programa de ruby: "Me gusta porque tiene metanfetamina"
vidya sagar
82

Python - 16

o cualquier otro idioma con eval.

exec"print 1;"*9
Vectorizado
fuente
¿Puedes describir lo que hace tu programa?
CailinP
10
Toma una cadena ( "print 1;"), la duplica 9 veces ( *9), luego ejecuta la cadena resultante ( exec). Repetir un fragmento de código sin hacer un bucle.
scragar
12
¡Yay por la multiplicación de cuerdas!
Thane Brimhall
2
También funciona en Ruby si cambia el execto eval o el printto echo.
Ajedi32
80

CSharp

He expandido el código de una manera más legible, ya que esto ya no es un código de golf y he agregado un contador de incrementos para que la gente pueda ver que este programa hace algo.

class P{
    static int x=0;
    ~P(){
        System.Console.WriteLine(++x);
        new P();
    }
    static void Main(){
        new P();
    }
}

(No hagas esto nunca por favor).

Al inicio, creamos una nueva instancia de la Pclase, que cuando el programa intenta salir llama al GC que llama al finalizador que crea una nueva instancia de la Pclase, que cuando intenta limpiar crea una nueva Pque llama al finalizador. .

El programa finalmente muere.

Editar: Inexplicablemente, esto solo se ejecuta alrededor de 45k veces antes de morir. No sé cómo el GC descubrió mi complicado bucle infinito, pero lo hizo. El resumen es que parece que no lo resolvió y que el hilo simplemente se cortó después de aproximadamente 2 segundos de ejecución: https://stackoverflow.com/questions/24662454/how-does-a-garbage-collector-avoid-an -infinite-loop-here

Edit2: si crees que esto es demasiado parecido a la recursión, considera mi otra solución: https://codegolf.stackexchange.com/a/33268/23300

Utiliza la reificación de métodos genéricos para que en tiempo de ejecución genere constantemente nuevos métodos y cada método en término llama a un método nuevo. También evito usar referenceparámetros de tipo, ya que normalmente el tiempo de ejecución puede compartir el código para esos métodos. Con un valueparámetro de tipo, el tiempo de ejecución se ve obligado a crear un nuevo método.

Michael B
fuente
20
Ni siquiera sabía que C # tenía destructores. +1 por enseñarme.
seequ
44
@TheRare, lo hace, pero son de naturaleza no determinista y nunca pueden ser llamados durante la ejecución de un programa. Se implementan como una anulación del método virtual, Finalizepor lo que a veces se les llama finalizer. En C # real, debe usar el IDisposablepatrón.
Michael B
Parece que hay un tiempo de espera que ocurre en algún momento. No creo que sea el GC el que esté deteniendo su ciclo, sino el sistema operativo que decide que su programa tarda demasiado en finalizar.
LVBen
Creo que este es realmente el tiempo de ejecución que decide matar el programa, no necesariamente el sistema operativo. El hilo del recolector de basura llamado al final del programa tiene un límite de tiempo fijo de ~ 2 segundos antes de que se elimine.
Michael B
Con algunas modificaciones menores (no dejar que el programa finalice, soltar el primer objeto P en el GC y llamar repetidamente a GC.Collect), puedo hacer que se ejecute indefinidamente.
LVBen
53

Befunge

.

El viejo Befunge sale 0 (de una pila vacía) casi para siempre, a medida que las líneas se envuelven.

Le Canard fou
fuente
1
¡Decir ah! Me encantan los trucos como este
CailinP
47

JS

(f=function(){ console.log('hi!'); eval("("+f+")()") })()

Fun fun!

Una función que crea otra función con el mismo cuerpo que ella misma y luego la ejecuta.

Mostrará hola al final cuando se alcance el límite de la pila y todo se derrumbe.

Descargo de responsabilidad: no podrá hacer nada en su navegador hasta que se alcance el límite de pila.


Y otro, más malvado :

function f(){ var tab = window.open(); tab.f = f; tab.f()}()

Crea una función que abre una ventana, luego crea una función dentro de esa ventana que es una copia de la función y luego la ejecuta.

Descargo de responsabilidad: si va a permitir la apertura de ventanas emergentes, la única forma de terminar esto será reiniciando su computadora

eithed
fuente
55
Esto es bastante mal seguro;)
CailinP
28
@CailinP Pretty eval seguro.
seequ
Creo que te estás perdiendo un fal final de tu segunda función. Debería ser }f()al final.
Chirag Bhatia - chirag64
2
Desafortunadamente, lo noté porque lo probé. : P
Chirag Bhatia - chirag64
77
-1: esto es solo recursividad.
user253751
39

Ensamblaje x86 / DOS

    org 100h

start:
    mov dx,data
    mov ah,9h
    int 21h
    push start
    ret

data:
    db "Hello World!",10,13,"$"

¿Dije que no hay recursión inversa de la cola ? ¿Hice? Madame mim dragones morados

Cómo funciona

La retinstrucción, utilizada para regresar de una función, en realidad saca la dirección de retorno de la pila (que normalmente se coloca allí por el correspondiente call) y salta a ella. Aquí en cada iteración tenemos pushla dirección del punto de entrada en la pila antes de regresar, generando así un bucle infinito.

Matteo Italia
fuente
Me preguntaba si esto era posible en la asamblea.
Ian D. Scott
2
Meterse con la pila a su propio riesgo . Here be dragons ;-)
Digital Trauma
1
Iba a llamar a codegolf.stackexchange.com/a/34295/11259 como un duplicado de este, pero veo que esa es en realidad la respuesta anterior
Digital Trauma
@DigitalTrauma: sí, me di cuenta después de que publiqué mi entrada, pero me puse adjunto a la imagen de Madame Mim. :-) Afortunadamente, hay algunas diferencias (la suya es un poco más confusa y funciona en Linux de 32 bits, la mía se juega directamente en DOS y no tiene otro salto), si nadie tiene alguna objeción, dejaría esto aquí de todos modos.
Matteo Italia
@MatteoItalia No ofuscado, simplemente horrible;) (aunque "agregar eax, 4" también me confundió, no pude encontrar la tabla de tamaños de código de operación, así que simplemente adiviné el tamaño). Lo hice en el compilador en línea mientras mi proyecto de trabajo se estaba compilando, por lo que se ve horrible. Gran idea de usar "inicio:".
PTwr
37

Java

Directamente desde XKCD

Vinculación

¡Es un juego interminable de captura entre padres e hijos!

El objetivo de CHILDse establece en PARENTy el objetivo de PARENTes CHILD. Cuando las PARENTllamadas AIM, arroja la instancia de la BALLclase y es capturada por la instrucción catch. La instrucción catch luego llama a PARENT.TARGET.AIMdonde está el objetivo CHILD. La CHILDinstancia hace lo mismo y "devuelve la pelota" al padre.

Kirk Backus
fuente
3
Me gustan esos comics!
Derek 朕 會 功夫
1
Sería mejor si la pelota fuera realmente lanzada entre el padre y el niño. Tal como está, la pelota siempre es lanzada y atrapada por la misma "persona".
Ajedi32
@ Ajedi32 En realidad, parecería que lo tira de un lado a otro; TARGET de los padres es el niño, y el objetivo del niño es el padre. Se llama al padre, quien agita la pelota y hace que el niño apunte y lance la pelota, cue loop
Alex Coleman
12
@AlexColeman Este código es análogo a que el padre arroje la pelota al aire, la atrape y luego se la entregue al niño que hace lo mismo antes de devolver la pelota al padre, y así sucesivamente.
Ajedi32
11
El comando TARGET.AIM(B);en método AIMes una llamada recursiva. Esto viola la regla "las funciones no pueden llamarse a sí mismas".
Theodore Norvell
31

Bash, 3 personajes

yes

yes repetidamente devolverá 'y' a la consola

Editar: se alienta a todos a editar esta línea:

yes something | xargs someaction

(gracias a Olivier Dulac)

PrimoCocaína
fuente
1
¿Por qué esto seguirá funcionando? No lo estoy cuestionando solo tratando de entender por qué.
Teun Pronk
2
@TeunPronk yeses un comando bash que imprime la palabra yes hasta que se elimina o la secuencia se cierra. Si está escribiendo en la pantalla, nunca se detendrá hasta que lo mate. Sin embargo, es una especie de trampa, ya que es un comando que básicamente consiste en un bucle sobre printf.
scragar
1
Sería más divertido usarlo yespara mantener en funcionamiento algún otro ciclo.
Trly
3
@izkata: pero luego puedes yes something | xargs someaction:: sin recursión (incluso puedes agregar -n 1 a xargs para tener solo 1 "algo" por línea, etc.). El uso de xargs abre el camino para comportamientos más complejos (incluso aquellos que no tienen nada que ver con el resultado sí)
Olivier Dulac
44
@scragar deberías haber respondido yes.
daviewales
28

C, 35 caracteres

main(int a,char**v){execv(v[0],v);}

El programa se ejecuta solo. No estoy seguro de si esto se considera recurrencia o no.

kwokkie
fuente
44
@mniip Tail recursion, entonces, si se aplica a nivel de proceso
Izkata
3
La recursión de @Izkata Tail sigue siendo recursiva, pero esto no es recursiva. La recursión implica una función (o proceso en este caso) 'esperando' a que termine otra iteración de sí misma. En este caso, exechace que el nuevo proceso reemplace al original, por lo que no hay una pila de llamadas que eventualmente regrese o se desborde.
millinon
44
@millinon En un lenguaje que admite la optimización, la recursión de cola reemplaza la llamada anterior en la pila de llamadas, de forma similar a cómo execreemplaza el proceso anterior. Tampoco se desbordará.
Izkata
1
@millinon solo para ser súper pedante y para prolongar esta discusión por más tiempo, en el lenguaje de programación Scheme, esta optimización es una característica del lenguaje. Está en la especificación de que si se hace una llamada recursiva de cola, el intérprete / compilador tiene que volver a utilizar el último marco de pila. Esto se debe a que Scheme no tiene estructuras de bucle incorporadas, por lo que la única forma de implementar un bucle en Scheme es hacer una recursión de cola, y sería un asco si tienes desbordamientos de pila al intentar buclear demasiadas veces :)
Ord
2
Si desea pedantería, Scheme no tiene "optimización de llamadas de cola", tiene "llamadas de cola adecuadas". No es una optimización, es un requisito básico del estándar de lenguaje y no se permite no proporcionarlo, por lo que "optimización" (o la sugerencia de que tiene algo que ver con la recursión) es un término muy desaconsejado.
Leushenko
28

C (con GCC incorporado - también parece funcionar con el sonido metálico)

  • No hay bucles explícitos
  • No hay gotos explícitos
  • Sin recursividad
  • Simplemente un buen juego anticuado con la pila (niños, no intenten esto en casa sin supervisión):
#include <stdio.h>

void *frameloop (void *ret_addr) {
    void **fp;
    void *my_ra = __builtin_return_address(0);

    if (ret_addr) {
        fp = __builtin_frame_address(0);
        if (*fp == my_ra) return (*fp = ret_addr);
        else fp++;
        if (*fp == my_ra) return (*fp = ret_addr);
        else fp++;
        if (*fp == my_ra) return (*fp = ret_addr);
        else fp++;
        if (*fp == my_ra) return (*fp = ret_addr);
        return NULL;
    } else {
        return (my_ra);
    }
}

int main (int argc, char **argv) {
    void *ret_addr;
    int i = 0;

    ret_addr = frameloop(NULL);
    printf("Hello World %d\n", i++);
    if (i < 10) {
        frameloop(ret_addr);
    }
}

Explicación:

  • main()primeras llamadas frameloop(NULL). En este caso, use el __builtin_return_address()incorporado para obtener la dirección de retorno (in main()) a la que frameloop()volverá. Le devolvemos esta dirección.
  • printf() para mostrar que estamos girando
  • ahora llamamos frameloop()con la dirección de retorno de la llamada anterior. Buscamos en la pila la dirección de retorno actual y, cuando la encontramos, sustituimos la dirección de retorno anterior.
  • Luego regresamos de la segunda frameloop()llamada. Pero dado que la dirección de devolución fue pirateada arriba, terminamos regresando al punto en main()el que debería regresar la primera llamada. Así terminamos en un bucle.

La búsqueda de la dirección de retorno en la pila sería, por supuesto, más limpia como un bucle, pero desenrollé algunas iteraciones en aras de que no haya ningún bucle.

Salida:

$ CFLAGS=-g make frameloop
cc -g    frameloop.c   -o frameloop
$ ./frameloop 
Hello World 0
Hello World 1
Hello World 2
Hello World 3
Hello World 4
Hello World 5
Hello World 6
Hello World 7
Hello World 8
Hello World 9
$ 
Trauma digital
fuente
2
¡bonito! Me pregunto por qué esas funciones no son parte de la especificación C? ;-D
Brian Minton
44
@BrianMinton En realidad, se debería lograr algo similar con setjmp()/ longjmp(). Estos no están en el estándar c, pero están en la biblioteca estándar . Sin embargo, tuve ganas de mezclar la pila manualmente hoy ;-)
Digital Trauma
@BrianMinton Supongo que es porque está en las especificaciones de la CPU, lo que lo hace dependiente de la plataforma (hardware). Y es bastante peligroso usarlo cuando stackframe se genera automáticamente, no me sorprendería si AV llorara por ese código. Verifique esto o esto para las versiones x86 asm.
PTwr
27

Haskell

El siguiente código no contiene ninguna función recursiva (incluso indirectamente), no tiene primitiva de bucle y no llama a ninguna función recursiva incorporada (solo usa IOla salida y el enlace), pero repite una acción dada de manera indefinida:

data Strange a = C (Strange a -> a)

-- Extract a value out of 'Strange'
extract :: Strange a -> a
extract (x@(C x')) = x' x

-- The Y combinator, which allows to express arbitrary recursion
yc :: (a -> a) -> a
yc f =  let fxx = C (\x -> f (extract x))
        in extract fxx

main = yc (putStrLn "Hello world" >>)

La función extractno llama a nada, ycsolo llama extracty mainllama solo ycy putStrLny >>, que no son recursivas.

Explicación: El truco está en el tipo de datos recursivo Strange. Es un tipo de datos recursivo que se consume a sí mismo, lo que, como se muestra en el ejemplo, permite la repetición arbitraria. Primero, podemos construir extract x, que esencialmente expresa la auto aplicación x xen el cálculo lambda sin tipo. Y esto permite construir el combinador Y definido como λf.(λx.f(xx))(λx.f(xx)).


Actualización: como se sugiere, estoy publicando una variante que está más cerca de la definición de Y en el cálculo lambda sin tipo:

data Strange a = C (Strange a -> a)

-- | Apply one term to another, removing the constructor.
(#) :: Strange a -> Strange a -> a
(C f) # x = f x
infixl 3 #

-- The Y combinator, which allows to express arbitrary recursion
yc :: (a -> a) -> a
yc f =  C (\x -> f (x # x)) # C (\x -> f (x # x))

main = yc (putStrLn "Hello world" >>)
Petr Pudlák
fuente
3
Estructuras de datos recursivas en lugar de funciones recursivas ... agradable.
ApproachingDarknessFish
66
este está cerca de mi corazón siendo alguien que está interesado en la programación funcional total. Acabas de mostrar cómo hacer el combinador Y con un tipo de datos que se repite negativamente. Esta es la razón por la cual los idiomas totales requieren que los tipos recurrentes ocurran a la derecha de la flecha y por qué no se permiten los rosales. ¡Buena esa! ¡Hice una cuenta aquí solo para votar esto!
Jake
Podría eliminar el letenlace y definirlo yc f = extract $ C $ f.extract, ya letque podría decirse que es una característica del lenguaje que permite la recursividad (la clásica let x = x in x). Esto también reduce algunos caracteres :)
Earth Engine
o inclusoyc = extract . C . (.extract)
Earth Engine
@EarthEngine Cierto, solo quería mantener la estructura más cerca de la definición original de Y.
Petr Pudlák
26

C ++

Lo siguiente genera una cuenta regresiva de 10 a "¡Despegue!" usando metaprogramación de plantilla.

#include <iostream>

template<int N>
void countdown() {
    std::cout << "T minus " << N << std::endl;
    countdown<N-1>();
}

template<>
void countdown<0>() {
    std::cout << "Blast off!" << std::endl;
}

int main()
{
    countdown<10>();
    return 0;
}

Puede parecer un ejemplo clásico de recursión, pero en realidad no lo es, al menos técnicamente, según su definición. El compilador generará diez funciones diferentes . countdown<10>imprime "T menos 10" y luego llama countdown<9>, y así sucesivamente hasta countdown<0>, que imprime "¡Despegue!" y luego regresa La recursión ocurre cuando compila el código, pero el ejecutable no contiene ninguna estructura de bucle.

En C ++ 11 se pueden lograr efectos similares usando la constexprpalabra clave, como esta función factorial. (No es posible implementar el ejemplo de cuenta regresiva de esta manera, ya que las constexprfunciones no pueden tener efectos secundarios, pero creo que podría ser posible en el próximo C ++ 14).

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n-1));
}

De nuevo, esto realmente se parece a la recursividad, pero el compilador se expandirá a cabo factorial(10)en 10*9*8*7*6*5*4*3*2*1, y luego, probablemente, reemplazarlo con un valor constante de 3628800, por lo que el ejecutable no contendrá ningún bucle o código recursivo.

Nathaniel
fuente
44
El segundo de estos realmente es pura y simple recursión, no metaprogramación. En primer lugar, porque el compilador emitirá (en el caso general) un cuerpo de función regular para que lo use con argumentos no constantes; y en segundo lugar, porque cuando realiza una operación en tiempo de compilación, no realiza ninguna de esas cosas de "expansión" estilo plantilla, ejecuta una evaluación estándar in situ, la misma que la de tiempo de ejecución, para producir 3628800directamente sin un forma intermedia
Leushenko
@Leushenko, sí, lo sé. Pero, de nuevo, el ejemplo de ejemplo de plantilla hace lo mismo: usa una función recursiva en un lenguaje completo de Turing en tiempo de compilación; la única diferencia es que constexpr usa un lenguaje que se parece mucho más a C ++. Al igual que con todas las respuestas, esta dobla las reglas, y estoy siendo honesto al respecto. constexprfue diseñado específicamente para hacer que (algunos aspectos de) la metaprogramación de plantillas sea obsoleta, por lo que definitivamente vale la pena mencionarlo en una publicación sobre el tema.
Nathaniel
1
1: &countdown<N-1> != &countdown<N>.
Thomas Eding
21

Java

Juguemos con el cargador de clases Java y configúrelo como su propio padre:

import java.lang.reflect.Field;

public class Loop {
    public static void main(String[] args) throws Exception {
        System.out.println("Let's loop");
        Field field = ClassLoader.class.getDeclaredField("parent");
        field.setAccessible(true);
        field.set(Loop.class.getClassLoader(), Loop.class.getClassLoader());

    }
}

Este bucle es realmente tan fuerte que tendrá que usar un kill -9para detenerlo :-)

Utiliza el 100,1% de la CPU de mi Mac.

100,1% de CPU

Puede intentar mover System.outal final de la función principal para experimentar un comportamiento divertido alternativo.

Arnaud
fuente
jajaja conseguir Java atascado en sí mismo :)
masterX244
Me encanta el truco de carga recursiva JVM.
Isiah Meadows
20

CSharp

Uno más e igualmente malvado ::

public class P{

    class A<B>{
        public static int C<T>(){
            System.Console.WriteLine(typeof(T));
            return C<A<T>>();
        }
    }
    public static void Main(){
        A<P>.C<int>();
    }
}

Esto no es recursividad ... esta es la reificación de plantillas de código. Si bien parece que estamos llamando al mismo método, el tiempo de ejecución crea constantemente nuevos métodos. Usamos el parámetro tipo de int, ya que esto realmente lo obliga a crear un tipo completamente nuevo y cada instancia del método tiene que crear un nuevo método. No puede compartir código aquí. Eventualmente, matamos la pila de llamadas mientras espera infinitamente el regreso de int que prometimos pero que nunca cumplimos. De manera similar, seguimos escribiendo el tipo que creamos para mantenerlo interesante. Básicamente, cada C que llamamos es un método totalmente nuevo que solo tiene el mismo cuerpo. Esto no es realmente posible en un lenguaje como C ++ o D que hace sus plantillas en tiempo de compilación. Como C # JIT es súper vago, solo crea estas cosas en el último momento posible. Así,

Michael B
fuente
14

Redcode 94 (Core War)

MOV 0, 1

Copia la instrucción en la dirección cero a la dirección uno. Debido a que en Core War todas las direcciones son relativas a la dirección actual de la PC y modulan el tamaño del núcleo, este es un bucle infinito en una sola instrucción sin salto.

Este programa (guerrero) se llama " Imp " y fue publicado por primera vez por AK Dewdney.

fresa
fuente
3
Los diablillos marcharán, alistan sus puertas, alísenlas o serán aplastados.
seequ
Prepara tu SPL 0, 0; MOV 1, -2verdad.
wberry
Agradable, esperaba que esto aún no se hubiera publicado. +1
mbomb007
14

Dardo

Supongo que esta sería la forma clásica de hacer recursividad sin ninguna función recursiva real. Ninguna función a continuación se refiere a sí misma por su nombre, directa o indirectamente.

(Pruébelo en dartpad.dartlang.org )

// Strict fixpoint operator.
fix(f) => ((x)=>f(x(x))) ((x)=>(v)=>f(x(x))(v));
// Repeat action while it returns true.
void repeat(action) { fix((rep1) => (b) { if (b()) rep1(b); })(action); }

main() {
  int x = 0;
  repeat(() {  
    print(++x);
    return x < 10;
  });
}
lrn
fuente
66
El combinador Y?
aditsu
55
Técnicamente, supongo que es el combinador Z porque es para un lenguaje estricto. El combinador Y requiere un lenguaje perezoso para evitar el despliegue infinito. La única diferencia es que la última parte está expandida eta.
lrn
12

JS

No muy original pero pequeño. 20 caracteres

setInterval(alert,1)
xem
fuente
De hecho, puede eliminar ,1y aún funcionará,
Derek 朕 會 功夫
@Derek 朕 會 功夫 si hago eso, solo recibo una alerta en Firefox
xem
1
En Chrome funciona sin el último parámetro. El código debe contarse como válido si funciona en al menos un entorno.
Derek 朕 會 功夫
3
Sin embargo, @Derek 朕 會 功夫setIntervalno es una declaración; Es solo una función. Se usa dentro de una declaración de expresión, y si no podemos usar declaraciones de expresión, entonces ni siquiera lo sé.
Keen
1
@Cory - ¡Bueno, supongo que es válido entonces!
Derek 朕 會 功夫
12

Señales en C

#include <stdio.h>
#include <signal.h>

int main(void) {
    signal(SIGSEGV, main);
    *(int*)printf("Hello, world!\n") = 0;
    return 0;
}

El comportamiento de este programa obviamente es muy indefinido, pero hoy, en mi computadora, sigue imprimiendo "¡Hola, mundo!".

Thomas Padron-McCarthy
fuente
11

Emacs Lisp

Este es un buen momento para mostrar el poderoso diseño de Lisp donde "el código es datos y los datos son código". Por supuesto, estos ejemplos son muy ineficientes y esto nunca debe usarse en un contexto real.

Las macros generan código que es una versión desenrollada del supuesto bucle y ese código generado es lo que se evalúa en tiempo de ejecución.

Repetirlo: le permite recorrer N veces

(defmacro repeat-it (n &rest body)
  "Evaluate BODY N number of times.
Returns the result of the last evaluation of the last expression in BODY."
  (declare (indent defun))
  (cons 'progn (make-list n (cons 'progn body))))

prueba de repetición:

;; repeat-it test
(progn
  (setq foobar 1)

  (repeat-it 10
    (setq foobar (1+ foobar)))

  ;; assert that we incremented foobar n times
  (assert (= foobar 11)))

repetir-con-índice:

Esta macro es similar, repeat-itpero en realidad funciona igual que la macro de bucle común, do-timesle permite especificar un símbolo que estará vinculado al índice del bucle. Utiliza un símbolo de tiempo de expansión para garantizar que la variable de índice esté configurada correctamente al comienzo de cada ciclo, independientemente de si modifica o no su valor durante el cuerpo del ciclo.

(defmacro repeat-it-with-index (var-and-n &rest body)
  "Evaluate BODY N number of times with VAR bound to successive integers from 0 inclusive to n exclusive..
VAR-AND-N should be in the form (VAR N).
Returns the result of the last evaluation of the last expression in BODY."
  (declare (indent defun))
  (let ((fallback-sym (make-symbol "fallback")))
    `(let ((,(first var-and-n) 0)
           (,fallback-sym 0))
       ,(cons 'progn
              (make-list (second var-and-n)
                         `(progn
                            (setq ,(first var-and-n) ,fallback-sym)
                            ,@body
                            (incf ,fallback-sym)))))))

prueba de repetir con índice:

Esta prueba muestra que:

  1. El cuerpo evalúa N veces

  2. la variable de índice siempre se establece correctamente al comienzo de cada iteración

  3. cambiar el valor de un símbolo llamado "reserva" no alterará el índice

;; repeat-it-with-index test
(progn
  ;; first expected index is 0
  (setq expected-index 0)

  ;; start repeating
  (repeat-it-with-index (index 50)
    ;; change the value of a  'fallback' symbol
    (setq fallback (random 10000))
    ;; assert that index is set correctly, and that the changes to
    ;; fallback has no affect on its value
    (assert (= index expected-index))
    ;; change the value of index
    (setq index (+ 100 (random 1000)))
    ;; assert that it has changed
    (assert (not (= index expected-index)))
    ;; increment the expected value
    (incf expected-index))

  ;; assert that the final expected value is n
  (assert (= expected-index 50)))
Jordon Biondo
fuente
11

Cálculo lambda sin tipo

λf.(λx.f (x x)) (λx.f (x x))
Arthur B
fuente
3
No estoy seguro de si esto cuenta como recursividad o no, ya que es la base teórica fundamental para ello ... +1 de todos modos.
esponjoso
@fluffy No es recursivo en sí mismo, ninguna de las funciones se llama a sí misma (particularmente porque las funciones no tienen nombre).
orgulloso Haskeller
En mi humilde opinión, el cálculo lambda es un modelo de cálculo y no es un lenguaje de programación (es decir, sin un modelo de máquina concreto, no podemos considerar el cálculo lambda como un PL).
Ta Thanh Dinh
Puede construir absolutamente una máquina que interprete el cálculo lambda. Y la sintaxis se puede usar como lenguaje de programación. Ver por ejemplo github.com/MaiaVictor/caramel
Arthur B
10

Haskell, 24 personajes

sequence_ (repeat (print "abc"))

o en forma condensada, con 24 caracteres

sequence_$repeat$print"" 

(aunque se cambia el texto, esto seguirá en bucle; esto imprimirá dos comillas y una nueva línea infinitamente)

explicación: print "abc" es básicamente una acción de E / S que simplemente imprime "abc".
repetir es una función que toma un valor x y devuelve una lista infinita hecha de solo x.
secuencia_ es una función que toma una lista de acciones de E / S y devuelve una acción de E / S que realiza todas las acciones secuencialmente.

así que, básicamente, este programa hace una lista infinita de comandos de impresión "abc" y los ejecuta repetidamente. sin bucles ni recursividad.

orgulloso Haskeller
fuente
44
Iba a publicar básicamente la misma respuesta en Clojure, pero pensé repeatque sería a programming language statement which allows code to be repeatedly executed.
seequ
3
fix(print"">>), esto tampoco implica funciones de repetición explícitamente nombradas.
mniip
1
@TheRare No sé cómo se está cerrando, pero en Haskell, la repetición no es "una declaración de lenguaje de programación que permita que el código se ejecute repetidamente", es una función que genera listas infinitas. es un bucle igual que "int [] arr = {x, x, x};" Es un bucle.
orgulloso Haskeller
1
Sí, pero algo debe implementarse utilizando la recursividad, porque sin ella es básicamente imposible
orgulloso Haskeller
3
En realidad, cada función que hay en este código se define usando la recursión, incluso la impresión
orgulloso haskeller
10

ASM (x86 + E / S para Linux)

No importa cuánto batallarán sus insignificantes lenguajes de alto nivel, seguirá siendo solo una manipulación oculta del puntero de instrucción. Al final, será una especie de "goto" (jmp), a menos que esté lo suficientemente aburrido como para desenrollar el bucle en tiempo de ejecución.

Puedes probar el código en Ideone

También puede consultar una versión más refinada de esta idea en el código DOS de Matteo Italia .

Comienza con una cadena de 0..9 y la reemplaza con A..J, no se utilizan saltos directos (así que digamos que no ocurrió "goto"), tampoco recurrencia.

El código probablemente podría ser más pequeño con algún cálculo de abuso de dirección, pero trabajar en el compilador en línea es molesto, así que lo dejaré como está.

Parte central:

mov dl, 'A' ; I refuse to explain this line!
mov ebx, msg ; output array (string)

call rawr   ; lets put address of "rawr" line on stack
rawr: pop eax ; and to variable with it! In same time we are breaking "ret"

add eax, 4 ; pop eax takes 4 bytes of memory, so for sake of stack lets skip it
mov [ebx], dl ; write letter
inc dl ; and proceed to next 
inc ebx
cmp dl, 'J' ; if we are done, simulate return/break by leaving this dangerous area
jg print

push eax ; and now lets abuse "ret" by making "call" by hand
ret

Código completo

section     .text
global      _start                              

_start:

;<core>
mov dl, 'A'
mov ebx, msg

call rawr
rawr: pop eax

add eax, 4
mov [ebx], dl
inc dl
inc ebx
cmp dl, 'J'
jg print

push eax
ret
;</core>

; just some Console.Write()
print:
    mov     edx,len
    mov     ecx,msg
    mov     ebx,1
    mov     eax,4
    int     0x80

    mov     eax,1
    xor     ebx, ebx
    int     0x80

section     .data

msg     db  '0123456789',0xa
len     equ $ - msg
PTwr
fuente
Iba a llamar esto como un duplicado de codegolf.stackexchange.com/a/34298/11259 , pero veo que esta es la respuesta anterior. +1
Trauma digital
@DigitalTrauma oh, veo que alguien hizo una versión refinada de mi idea: un viejo truco, pero en la era del código administrado la gente tiende a olvidar cómo funcionan realmente las cosas. (No me gusta el golf, con demasiada frecuencia se reduce a "¡mira mamá! ¡Puedo hacer que las cosas sucedan presionando una tecla!")
PTwr
9

C preprocesador

Una pequeña "técnica" que se me ocurrió durante un desafío de ofuscación. No hay recursión de funciones, pero hay ... ¿recursividad de archivos?

noloop.c:

#if __INCLUDE_LEVEL__ == 0
int main() 
{
    puts("There is no loop...");
#endif
#if __INCLUDE_LEVEL__ <= 16
    puts(".. but Im in ur loop!");
    #include "noloop.c"
#else
    return 0;
}
#endif

Escribí / probé esto usando gcc. Obviamente, su compilador debe admitir la __INCLUDE_LEVEL__macro (o, alternativamente, la __COUNTER__macro con algunos ajustes) para que esto se compile. Debería ser bastante obvio cómo funciona esto, pero por diversión, ejecute el preprocesador sin compilar el código (use el -Eindicador con gcc).

Acercarse Oscuridad Peces
fuente
8

PHP

Aquí hay uno con PHP. Incluye bucles al incluir el mismo archivo hasta que el contador alcance $ max:

<?php
if (!isset($i))
    $i = 0;        // Initialize $i with 0
$max = 10;         // Target value

// Loop body here
echo "Iteration $i <br>\n";

$i++;               // Increase $i by one on every iteration

if ($i == $max)
    die('done');    // When $i reaches $max, end the script
include(__FILE__);  // Proceed with the loop
?>

Lo mismo que un bucle for:

<?php
for ($i = 0; $i < 10; $i++) {
    echo "Iteration $i <br>\n";
}
die('done');
?>
Pichan
fuente
Maldición, esto también cuenta como recursión, ¿no?
Pichan
No piense que es así, la similitud viene a la mente del ejemplo de @ Nathaniel: el preprocesador incluirá estos archivos que luego se evaluarán simultáneamente.
Eithed
@ Pichan, diría que es más un despliegue de bucles, ya que terminas con copias de código en la memoria.
PTwr
Acabo de ver la pregunta hoy y se me ocurrió un código casi idéntico. ¡Demasiado tarde para mi!
TecBrat
¿Se header("Location: .?x=".$_GET['x']+1);cuenta como recursividad?
Charlie
8

Pitón

El siguiente código no contiene ninguna función recursiva (directa o indirecta), no tiene primitiva de bucle y no llama a ninguna función incorporada (excepto print):

def z(f):
    g = lambda x: lambda w: f(lambda v: (x(x))(v), w)
    return g(g)

if __name__ == "__main__":
    def msg(rec, n):
        if (n > 0):
            print "Hello world!"
            rec(n - 1)
    z(msg)(7)

Imprime "¡Hola mundo!" un número dado de veces

Explicación: La función zimplementa el estricto combinador de punto fijo Z , que (aunque no está definido recursivamente) permite expresar cualquier algoritmo recursivo.

Petr Pudlák
fuente
Yo llamaría gmucho indirectamente recursivo.
seequ
@TheRare ¿Por qué? ¿Cuál es tu argumento? ¿Qué gllama eso que llama de gnuevo? Por supuesto que el truco es la autoaplicación g(g), pero no hay recurrencia involucrada. ¿Realmente llamarías gindirectamente recursivo si no lo has visto g(g)? Esta es la forma estándar de hacerlo en idiomas que no permiten definiciones recursivas, como el cálculo lambda.
Petr Pudlák
Usted da gcomo argumento xy luego llama x(x).
seequ
2
@TheRare Una función (o un conjunto de funciones) no es recursiva o no recursiva por cómo se usa, esto se determina solo por su definición.
Petr Pudlák
1
todas las respuestas engañan de una forma u otra: siempre hay recursividad o un bucle en algún lugar , si no en la respuesta, entonces en el código se invoca la respuesta. Me gusta la forma en que este engaña.
Wayne Conrad
8

código máquina z80

En un entorno donde puede ejecutar en cada dirección y asignar ROM en cualquier lugar, asigne 64kb de ROM llenos de ceros a todo el espacio de direcciones.

Lo que hace: nada. Repetidamente.

Cómo funciona: el procesador comenzará a ejecutarse, el byte 00es una nopinstrucción, por lo que simplemente continuará, alcanzará la dirección $ffff, se ajustará $0000y continuará ejecutando nops hasta que lo reinicie.

Para hacerlo un poco más interesante, llene la memoria con algún otro valor (tenga cuidado de evitar las instrucciones de control de flujo).

harold
fuente
Podrías llenar la memoria con ceros y colocar un programa real allí en alguna parte.
seequ
Entonces, ¿podría poner un programa de 64k, sin ramificación alguna, y simplemente se ejecutaría repetidamente?
Bill Woodger
@BillWoodger podría, especialmente si no tiene interrupciones en la plataforma (o ninguna habilitada)
harold
Tipo de diversión :-)
Bill Woodger
8

Perl-regex

(q x x x 10) =~ /(?{ print "hello\n" })(?!)/;

manifestación

o pruébalo como:

perl -e '(q x x x 10) =~ /(?{ print "hello\n" })(?!)/;'

El (?!)nunca coincide. Entonces, el motor de expresiones regulares intenta hacer coincidir cada posición de ancho cero en la cadena coincidente.

El (q x x x 10)es el mismo que (" " x 10)- repita las spacediez veces.

Editar: cambió los "caracteres" a posiciones de ancho cero para ser más precisos para una mejor comprensión. Vea las respuestas a esta pregunta de stackoverflow .

jm666
fuente
6

T-SQL -12

print 1
GO 9

En realidad, es más una peculiaridad de Sql Server Management Studio. GO es un separador de script y no forma parte del lenguaje T-SQL. Si especifica GO seguido de un número, ejecutará el bloque tantas veces.

Michael B
fuente
1
Utilizo T-SQL casi todos los días y no tenía idea de que podría hacer esto con GO. +1
CailinP
Técnicamente, eso no es T-SQL. GOes en realidad una directiva SSMS, por lo que no puede colocarla en objetos con script T-SQL, como por ejemplo un procedimiento almacenado.
RBarryYoung
Sí, lo agregué en mi comentario de spoiler. Me imagino que usar sqlcmd sería demasiado hacer trampa.
Michael B
6

C#

Imprime todos los enteros desde uint.MaxValue a 0.

   class Program
   {
      public static void Main()
      {
          uint max = uint.MaxValue;
          SuperWriteLine(ref max);
          Console.WriteLine(0);
      }

      static void SuperWriteLine(ref uint num)
      {
          if ((num & (1 << 31)) > 0) { WriteLine32(ref num); }
          if ((num & (1 << 30)) > 0) { WriteLine31(ref num); }
          if ((num & (1 << 29)) > 0) { WriteLine30(ref num); }
          if ((num & (1 << 28)) > 0) { WriteLine29(ref num); }
          if ((num & (1 << 27)) > 0) { WriteLine28(ref num); }
          if ((num & (1 << 26)) > 0) { WriteLine27(ref num); }
          if ((num & (1 << 25)) > 0) { WriteLine26(ref num); }
          if ((num & (1 << 24)) > 0) { WriteLine25(ref num); }
          if ((num & (1 << 23)) > 0) { WriteLine24(ref num); }
          if ((num & (1 << 22)) > 0) { WriteLine23(ref num); }
          if ((num & (1 << 21)) > 0) { WriteLine22(ref num); }
          if ((num & (1 << 20)) > 0) { WriteLine21(ref num); }
          if ((num & (1 << 19)) > 0) { WriteLine20(ref num); }
          if ((num & (1 << 18)) > 0) { WriteLine19(ref num); }
          if ((num & (1 << 17)) > 0) { WriteLine18(ref num); }
          if ((num & (1 << 16)) > 0) { WriteLine17(ref num); }
          if ((num & (1 << 15)) > 0) { WriteLine16(ref num); }
          if ((num & (1 << 14)) > 0) { WriteLine15(ref num); }
          if ((num & (1 << 13)) > 0) { WriteLine14(ref num); }
          if ((num & (1 << 12)) > 0) { WriteLine13(ref num); }
          if ((num & (1 << 11)) > 0) { WriteLine12(ref num); }
          if ((num & (1 << 10)) > 0) { WriteLine11(ref num); }
          if ((num & (1 << 9)) > 0) { WriteLine10(ref num); }
          if ((num & (1 << 8)) > 0) { WriteLine09(ref num); }
          if ((num & (1 << 7)) > 0) { WriteLine08(ref num); }
          if ((num & (1 << 6)) > 0) { WriteLine07(ref num); }
          if ((num & (1 << 5)) > 0) { WriteLine06(ref num); }
          if ((num & (1 << 4)) > 0) { WriteLine05(ref num); }
          if ((num & (1 << 3)) > 0) { WriteLine04(ref num); }
          if ((num & (1 << 2)) > 0) { WriteLine03(ref num); }
          if ((num & (1 <<  1)) > 0) { WriteLine02(ref num); }
          if ((num & (1 <<  0)) > 0) { WriteLine01(ref num); }
      }

      private static void WriteLine32(ref uint num) { WriteLine31(ref num); WriteLine31(ref num); }
      private static void WriteLine31(ref uint num) { WriteLine30(ref num); WriteLine30(ref num); }
      private static void WriteLine30(ref uint num) { WriteLine29(ref num); WriteLine29(ref num); }
      private static void WriteLine29(ref uint num) { WriteLine28(ref num); WriteLine28(ref num); }
      private static void WriteLine28(ref uint num) { WriteLine27(ref num); WriteLine27(ref num); }
      private static void WriteLine27(ref uint num) { WriteLine26(ref num); WriteLine26(ref num); }
      private static void WriteLine26(ref uint num) { WriteLine25(ref num); WriteLine25(ref num); }
      private static void WriteLine25(ref uint num) { WriteLine24(ref num); WriteLine24(ref num); }
      private static void WriteLine24(ref uint num) { WriteLine23(ref num); WriteLine23(ref num); }
      private static void WriteLine23(ref uint num) { WriteLine22(ref num); WriteLine22(ref num); }
      private static void WriteLine22(ref uint num) { WriteLine21(ref num); WriteLine21(ref num); }
      private static void WriteLine21(ref uint num) { WriteLine20(ref num); WriteLine20(ref num); }
      private static void WriteLine20(ref uint num) { WriteLine19(ref num); WriteLine19(ref num); }
      private static void WriteLine19(ref uint num) { WriteLine18(ref num); WriteLine18(ref num); }
      private static void WriteLine18(ref uint num) { WriteLine17(ref num); WriteLine17(ref num); }
      private static void WriteLine17(ref uint num) { WriteLine16(ref num); WriteLine16(ref num); }
      private static void WriteLine16(ref uint num) { WriteLine15(ref num); WriteLine15(ref num); }
      private static void WriteLine15(ref uint num) { WriteLine14(ref num); WriteLine14(ref num); }
      private static void WriteLine14(ref uint num) { WriteLine13(ref num); WriteLine13(ref num); }
      private static void WriteLine13(ref uint num) { WriteLine12(ref num); WriteLine12(ref num); }
      private static void WriteLine12(ref uint num) { WriteLine11(ref num); WriteLine11(ref num); }
      private static void WriteLine11(ref uint num) { WriteLine10(ref num); WriteLine10(ref num); }
      private static void WriteLine10(ref uint num) { WriteLine09(ref num); WriteLine09(ref num); }
      private static void WriteLine09(ref uint num) { WriteLine08(ref num); WriteLine08(ref num); }
      private static void WriteLine08(ref uint num) { WriteLine07(ref num); WriteLine07(ref num); }
      private static void WriteLine07(ref uint num) { WriteLine06(ref num); WriteLine06(ref num); }
      private static void WriteLine06(ref uint num) { WriteLine05(ref num); WriteLine05(ref num); }
      private static void WriteLine05(ref uint num) { WriteLine04(ref num); WriteLine04(ref num); }
      private static void WriteLine04(ref uint num) { WriteLine03(ref num); WriteLine03(ref num); }
      private static void WriteLine03(ref uint num) { WriteLine02(ref num); WriteLine02(ref num); }
      private static void WriteLine02(ref uint num) { WriteLine01(ref num); WriteLine01(ref num); }
      private static void WriteLine01(ref uint num) { Console.WriteLine(num--); }
   }
LVBen
fuente
1
Realmente no sé si esto cuenta. Está llamando explícitamente a WriteLine01 Int.MaxValue veces. Simplemente explotó detrás de una gran cantidad de callstack.
Michael B
¿Cómo no cuenta? No hay bucle ni recursividad.
LVBen
1
Además, la pila de llamadas no está cerca de ser masiva a menos que tal vez consideres que un alto de 32 llamadas es masivo.
LVBen
1
¿Por qué solo 32 veces en lugar de 4294967296 veces?
LVBen
44
@ ja72 Si alguna vez estoy trabajando en un proyecto de código abierto en el que no puedo usar bucles o recursividad, ¡voy a contribuir totalmente con un código como este!
LVBen
6

JS (en navegador)

¿Qué tal esto?

document.write(new Date());
location = location;

Imprime la hora actual y vuelve a cargar la página.

Pichan
fuente
Oh dispara. Acabo de publicar una respuesta con el mismo concepto básico. Había estado escaneando la página en busca de "JavaScript" o cualquier cosa que mostrara etiquetas HTML. Supongo que podría dejar mi respuesta, solo porque maneja el caso de la esquina donde la ubicación contiene un "#". De todos modos, +1.
Keen
En Firefox 30:[Exception... "The operation is insecure." code: "18" nsresult: "0x80530012 (SecurityError)" location: "<unknown>"]
Alex Reynolds
@AlexReynolds Huh, raro. El mío funciona bien en FF 30.
Pichan
Solo copié y pegué su código tal como estaba escrito. No funciona ¿Quizás tenga algunas preferencias de seguridad especiales habilitadas para que esto funcione?
Alex Reynolds
@AlexReynolds No, nunca cambió ninguna configuración de seguridad. Y también funciona en Chrome.
Pichan