Eine ähnliche Frage wurde vor ein paar Jahren gestellt , aber diese ist noch kniffliger.
Die Herausforderung ist einfach. Schreiben Sie ein Programm (in der Sprache Ihrer Wahl) , die wiederholt Code ausführt , ohne Wiederholung Strukturen unter Verwendung von wie while
, for
, do while
, foreach
oder goto
( also für alle Sie Nitpicker, man kann nicht eine Schleife verwenden ). Eine Rekursion ist jedoch in der Funktion, die sich selbst aufruft , nicht zulässig (siehe Definition unten) . Das würde diese Herausforderung viel zu einfach machen.
Es gibt keine Einschränkung, was in der Schleife ausgeführt werden muss, aber geben Sie eine Erklärung mit Ihrer Antwort an, damit andere genau verstehen, was implementiert wird.
Für diejenigen, die an Definitionen aufgehängt werden können, lautet die Definition einer Schleife für diese Frage:
A programming language statement which allows code to be repeatedly executed.
Und die Definition der Rekursion für diese Frage ist Ihre Standarddefinition für rekursive Funktionen:
A function that calls itself.
Gewinner ist die Antwort, die am 16. Juli um 10.00 Uhr Ortszeit die meisten positiven Stimmen hat. Viel Glück!
AKTUALISIEREN:
Zur Beruhigung der noch bestehenden Verwirrung kann Folgendes hilfreich sein:
Regeln wie oben angegeben:
- Benutze keine Schleifen oder gehe zu
- Funktionen können sich nicht selbst aufrufen
- Mach was du willst in der Schleife
Wenn Sie etwas implementieren möchten und die Regeln es nicht explizit verbieten, gehen Sie vor und tun Sie es. Viele Antworten haben die Regeln bereits verbogen.
function A
rufen Sie auffunction B
undfunction B
rufen Sie auf,function A
während eine der Funktionen etwas ausführt. Da sich die Funktion nicht selbst nennt, sollte sie nach den Kriterien ^. ^ Gültig seinrep(f){f();f();}
- Dies ist eine Anweisung (eine Funktionsdeklaration ist eine Anweisung in einigen Sprachen), mit der Code wiederholt ausgeführt werden kann. Ist es nicht erlaubt? Sie fragen nach Code, um eine Schleife zu implementieren. Wenn dieser Code syntaktisch eine Anweisung ist, haben Sie ihn gerade nicht zugelassen. Ein weiteres Beispiel:f(b) { b(); g(b); }; g(b) { f(b); }
. Ich würde sagen, esf
ist eine rekursive Funktion (indem man sich gegenseitig rekursiv behandeltg
). Ist es nicht erlaubt?Antworten:
Rubin
Demo
Räuspert sich, druckt 3070-mal "Banane" und schreibt auch "Orange, du bist froh, dass ich keine Banane gesagt habe?".
Dabei wird Rubys lächerliche Just-in-Time-Methodendefinitionsfunktion verwendet, um jede Methode zu definieren, die alphabetisch zwischen den Wörtern "ahem" und "also" ("ahem", "ahen", "aheo", "ahep", "aheq") liegt. "aher", "ahes", "ahet", "aheu", "ahev" ...), um zuerst die Banane zu drucken und dann die nächste in der Liste aufzurufen.
quelle
String#next
das inmethod_missing
Funktionen mehr oder weniger wie das Hinzufügen von 1 zu einer Zahl aufgerufen wird, funktioniert es jedoch mit allen alphanumerischen Zeichen (und Nicht-Alumni, wenn sie die einzigen Zeichen in der Zeichenfolge sind). Siehe ruby-doc.org/core-2.1.2/String.html#method-i-nextb.my_tag
. Es wird auch in ActiveRecord-Modellen oder verwendetOpenStruct
. In "Wat Talk" sagt er, dass globalmethod_missing
schlecht ist, aber der Umfang ist fantastisch.Python - 16
oder eine andere Sprache mit eval.
quelle
"print 1;"
), dupliziert ihn 9 Mal (*9
) und führt dann den resultierenden String (exec
) aus. Wiederholen eines Codeblocks, ohne überhaupt eine Schleife auszuführen.exec
zueval
oder dasprint
zu ändernecho
.CSharp
Ich habe den Code auf eine lesbarere Art und Weise erweitert, da dies kein Code-Golf mehr ist, und habe einen Inkrement-Zähler hinzugefügt, damit die Leute tatsächlich sehen können, dass dieses Programm etwas tut.
(Mach das bitte nie).
Beim Start erstellen wir eine neue Instanz der
P
Klasse, die beim Beenden des Programms den GC aufruft, der den Finalizer aufruft, der eine neue Instanz derP
Klasse erstellt, und beim Aufräumen eine neue Instanz erstellt,P
die den Finalizer aufruft. .Das Programm stirbt schließlich.
Bearbeiten: Aus unerklärlichen Gründen läuft dies nur etwa 45.000 Mal vor dem Sterben. Ich weiß nicht genau, wie der GC meine knifflige Endlosschleife herausgefunden hat, aber es hat geklappt. Der Kurzschluss ist, dass es den Anschein hat, als hätte er es nicht herausgefunden, und der Thread wurde nur nach ungefähr 2 Sekunden Ausführung beendet: https://stackoverflow.com/questions/24662454/how-does-a-garbage-collector-avoid-an Endlosschleife hier
Edit2: Wenn Sie der Meinung sind, dass dies ein bisschen zu viel Rekursion ähnelt, ziehen Sie meine andere Lösung in Betracht: https://codegolf.stackexchange.com/a/33268/23300
Es verwendet die Reifizierung generischer Methoden, sodass zur Laufzeit ständig neue Methoden generiert werden und jede Methode im Ausdruck eine neu erstellte Methode aufruft. Ich vermeide auch die Verwendung von
reference
Typparametern, da normalerweise die Laufzeitumgebung den Code für diese Methoden gemeinsam nutzen kann. Mit einemvalue
Typparameter wird die Laufzeit gezwungen, eine neue Methode anzulegen.quelle
Finalize
sodass sie manchmal aufgerufen werdenfinalizer
. In echtem C # sollten Sie dasIDisposable
Muster verwenden.Befunge
Gute alte Befunge gibt 0 (von einem leeren Stapel) so ziemlich für immer aus, während Zeilen umlaufen.
quelle
JS
(f=function(){ console.log('hi!'); eval("("+f+")()") })()
Funktionsspaß!
Eine Funktion, die eine andere Funktion mit demselben Körper wie sich selbst erstellt und dann ausführt.
Am Ende wird hi angezeigt, wenn das Stack-Limit erreicht ist und das Ganze zusammenbricht.
Haftungsausschluss: Sie können in Ihrem Browser nichts tun, bis das Stack-Limit erreicht ist.
Und noch einer, böser :function f(){ var tab = window.open(); tab.f = f; tab.f()}()
Es erstellt eine Funktion, die ein Fenster öffnet, erstellt dann eine Funktion in dem Fenster, das eine Kopie der Funktion ist, und führt sie dann aus.
Haftungsausschluss: Wenn Sie das Öffnen von Popups zulassen, können Sie dies nur beenden, indem Sie Ihren Computer neu startenquelle
f
am Ende Ihrer zweiten Funktion. Es sollte}f()
am Ende sein.x86-Assembly / DOS
Wie es funktioniert
quelle
Java
Direkt von XKCD
Es ist ein nie endendes Fangspiel zwischen Eltern und Kind!
Das Ziel von
CHILD
ist aufPARENT
und das Ziel vonPARENT
ist dasCHILD
. Wenn diePARENT
AnrufeAIM
, es die Instanz der wirftBALL
Klasse und wird von der catch - Anweisung gefangen. Die catch-Anweisung ruft dann auf,PARENT.TARGET.AIM
wo sich das Ziel befindetCHILD
. DieCHILD
Instanz macht dasselbe und "wirft den Ball zurück" zum Elternteil.quelle
TARGET.AIM(B);
in der MethodeAIM
ist ein rekursiver Aufruf. Dies verstößt also gegen die Regel "Funktionen können sich nicht selbst nennen".Bash, 3 Zeichen
yes gibt wiederholt "y" an die Konsole zurück
Bearbeiten: Jeder wird ermutigt, diese Zeile zu bearbeiten:
(Danke an Olivier Dulac)
quelle
yes
ist ein Bash-Befehl, der das Wort yes ausgibt, bis es beendet wird oder der Stream geschlossen wird. Wenn es auf den Bildschirm schreibt, hört es nie auf, bis Sie es töten. Es ist jedoch eine Art Betrug, da es sich um einen Befehl handelt, der im Grunde aus einer Schleife über printf besteht.yes
, eine andere Schleife am Laufen zu halten.yes something | xargs someaction
keine Rekursion (Sie können sogar -n 1 zu xargs hinzufügen, um nur 1 "etwas" pro Zeile usw. zu haben). Die Verwendung von xargs eröffnet den Weg für komplexere Verhaltensweisen (selbst diejenigen, die überhaupt nichts mit der Ja-Ausgabe zu tun haben)yes
.C, 35 Zeichen
Das Programm führt sich selbst aus. Ich bin nicht sicher, ob dies als Rekursion angesehen wird oder nicht.
quelle
exec
der ursprüngliche Prozess durch den neuen Prozess ersetzt, sodass es keinen Aufrufstapel gibt, der möglicherweise zurückkehrt oder überläuft.exec
beim vorherigen Vorgang. Es wird auch nicht überlaufen.C (mit GCC Builtins - scheint auch mit Clang zu funktionieren)
Erläuterung:
main()
erste Anrufeframeloop(NULL)
. In diesem Fall verwenden Sie das__builtin_return_address()
Built-In, um die Rücksprungadresse (inmain()
) abzurufen, zu der zurückgekehrtframeloop()
wird. Wir senden diese Adresse zurück.printf()
um zu zeigen, wir schleifenframeloop()
mit der rücksprungadresse für den vorigen anruf an. Wir durchsuchen den Stapel nach der aktuellen Absenderadresse und ersetzen die vorherige Absenderadresse, wenn wir sie finden.frameloop()
Anruf zurück. Da die Absenderadresse oben gehackt wurde, kehren wir zu dem Punkt zurück,main()
an den der erste Anruf zurückkehren soll. So geraten wir in eine Schleife.Die Suche nach der Absenderadresse im Stapel wäre natürlich sauberer als eine Schleife, aber ich habe ein paar Iterationen abgewickelt, um überhaupt keine Schleife zu haben.
Ausgabe:
quelle
setjmp()
/ eine ähnliche Sache erreichbar seinlongjmp()
. Diese befinden sich nicht im c-Standard, sondern in der Standardbibliothek . Ich wollte den Stapel heute allerdings manuell mischen ;-)Haskell
Der folgende Code enthält keine rekursive Funktion (auch nicht indirekt), kein Schleifenprimitiv und ruft keine eingebaute rekursive Funktion auf (verwendet nur
IO
die Ausgabe und die Bindung von), wiederholt jedoch eine gegebene Aktion endlos:Funktion
extract
ruft nichts auf,yc
ruft nurextract
undmain
ruft nuryc
undputStrLn
und auf>>
, was nicht rekursiv ist.Erläuterung: Der Trick liegt im rekursiven Datentyp vor
Strange
. Es ist ein rekursiver Datentyp, der sich selbst verbraucht, was, wie im Beispiel gezeigt, eine willkürliche Wiederholung ermöglicht. Zunächst können wir konstruierenextract x
, was im Wesentlichen die Selbstanwendungx x
im untypisierten Lambda-Kalkül ausdrückt . Und dies erlaubt es, den als definierten Y-Kombinator zu konstruierenλf.(λx.f(xx))(λx.f(xx))
.Update: Wie vorgeschlagen, veröffentliche ich eine Variante, die der Definition von Y im untypisierten Lambda-Kalkül näher kommt :
quelle
let
Bindung entfernen und definierenyc f = extract $ C $ f.extract
, da dielet
Bindung möglicherweise eine Sprachfunktion ist, die eine Rekursion ermöglicht (die klassischelet x = x in x
). Dies reduziert auch einige Zeichen :)yc = extract . C . (.extract)
Y
.C ++
Das Folgende gibt einen Countdown von 10 bis "Blast off!" mit Vorlage Metaprogrammierung.
Es mag wie ein klassisches Beispiel für Rekursion aussehen, ist aber, zumindest technisch, nicht von Ihrer Definition abhängig. Der Compiler generiert zehn verschiedene Funktionen.
countdown<10>
gibt "T minus 10" aus und ruft danncountdown<9>
und so weiter aufcountdown<0>
, was "Blast off!" und kehrt dann zurück. Die Rekursion tritt auf, wenn Sie den Code kompilieren, die ausführbare Datei enthält jedoch keine Schleifenstrukturen.In C ++ 11 können mit dem
constexpr
Schlüsselwort ähnliche Effekte erzielt werden, z. B. diese Fakultätsfunktion. (Es ist nicht möglich, das Countdown-Beispiel auf diese Weise zu implementieren, daconstexpr
Funktionen keine Nebenwirkungen haben können, aber ich denke, dass dies im kommenden C ++ 14 möglich sein könnte.)Auch hier sieht wirklich wie Rekursion, aber der Compiler erweitert heraus
factorial(10)
in10*9*8*7*6*5*4*3*2*1
, und dann wahrscheinlich mit einem konstanten Wert von ersetzen3628800
, so dass die ausführbaren Datei wird jedes Looping oder rekursive Code nicht enthalten.quelle
3628800
direkt ohne eine zu produzieren Zwischenform.constexpr
wurde speziell entwickelt, um (einige Aspekte von) Template-Metaprogrammierung überflüssig zu machen, daher scheint es definitiv erwähnenswert, in einem Beitrag zu diesem Thema erwähnt zu werden.&countdown<N-1> != &countdown<N>
.Java
Spielen wir mit dem Java-Klassenladeprogramm und legen es als eigenes übergeordnetes Element fest:
Diese Schleife ist tatsächlich so stark, dass man sie mit einem
kill -9
stoppen muss :-)Es nutzt 100,1% der CPU meines Mac.
Sie können versuchen, die Taste
System.out
am Ende der Hauptfunktion zu bewegen , um ein anderes lustiges Verhalten zu testen.quelle
CSharp
Eine weitere und ebenso böse:
Dies ist keine Rekursion ... dies ist die Reifizierung von Codevorlagen. Während es so aussieht, als ob wir dieselbe Methode aufrufen, erstellt die Laufzeit ständig neue Methoden. Wir verwenden den Typparameter von int, da dies tatsächlich dazu zwingt, einen völlig neuen Typ zu erstellen, und jede Instanz der Methode eine neue Methode erzeugen muss. Es kann hier keine Codefreigabe erfolgen. Schließlich beenden wir den Aufrufstapel, da er unendlich lange auf die Rückkehr von int wartet, die wir versprochen, aber nie geliefert haben. In ähnlicher Weise schreiben wir den Typ, den wir erstellt haben, um ihn interessant zu halten. Grundsätzlich ist jedes C, das wir aufrufen, eine völlig neue Methode, die nur den gleichen Körper hat. Dies ist in einer Sprache wie C ++ oder D, die ihre Vorlagen zur Kompilierungszeit erstellt, nicht wirklich möglich. Da C # JIT sehr faul ist, erstellt es dieses Zeug nur im letztmöglichen Moment. Somit,
quelle
Redcode 94 (Kernkrieg)
MOV 0, 1
Kopiert die Anweisung an der Adresse Null in die Adresse Eins. Da in Core War alle Adressen relativ zur aktuellen PC-Adresse sind und die Größe des Kerns modulieren, handelt es sich um eine Endlosschleife in einem Befehl, der kein Sprung ist.
Dieses Programm (Krieger) heißt " Imp " und wurde zuerst von AK Dewdney veröffentlicht.
quelle
SPL 0, 0; MOV 1, -2
Tat vor.Pfeil
Ich denke, dies wäre die klassische Art, eine Rekursion ohne tatsächliche rekursive Funktion durchzuführen. Keine der folgenden Funktionen bezieht sich direkt oder indirekt auf sich selbst.
(Versuchen Sie es unter dartpad.dartlang.org )
quelle
JS
Nicht sehr originell, aber klein. 20 Zeichen.
quelle
,1
und es wird immer noch funktionieren,setInterval
ist jedoch keine Aussage; Es ist nur eine Funktion. Es wird in einer expression-Anweisung verwendet, und wenn wir expression-Anweisungen nicht verwenden können, weiß ich es einfach nicht einmal mehr.Signale in C
Das Verhalten dieses Programms ist offensichtlich sehr undefiniert, aber heute druckt es auf meinem Computer immer noch "Hallo Welt!".
quelle
Emacs Lisp
Dies ist eine großartige Gelegenheit, um Lisps leistungsstarkes Design zu demonstrieren, bei dem "Code Daten und Daten Code sind". Zugegeben, diese Beispiele sind sehr ineffizient und sollten niemals in einem realen Kontext verwendet werden.
Die Makros generieren Code, der eine nicht gerollte Version der angenommenen Schleife ist, und dieser generierte Code wird zur Laufzeit ausgewertet.
repeat-it: Hiermit können Sie N-mal eine Schleife ausführen
Wiederholungstest:
Wiederholung mit Index:
Dieses Makro ähnelt
repeat-it
, funktioniert jedoch genauso wie das allgemeine Schleifenmakrodo-times
, mit dem Sie ein Symbol angeben können, das an den Schleifenindex gebunden wird. Mithilfe eines Erweiterungszeitsymbols wird sichergestellt, dass die Indexvariable am Anfang jeder Schleife korrekt festgelegt ist, unabhängig davon, ob Sie den Wert während des Schleifenkörpers ändern oder nicht.Wiederholungstest mit Index:
Dieser Test zeigt, dass:
Der Körper wertet N-mal aus
Die Indexvariable wird zu Beginn jeder Iteration immer korrekt gesetzt
Das Ändern des Werts eines Symbols mit dem Namen "Fallback" wirkt sich nicht auf den Index aus
quelle
Untypisierter Lambda-Kalkül
quelle
Haskell, 24 Zeichen
oder in komprimierter Form mit 24 Zeichen
(Obwohl der Text geändert wird, wird die Schleife fortgesetzt - dies gibt zwei Anführungszeichen und eine neue Zeile unendlich aus.)
Erklärung: print "abc" ist im Grunde eine I / O-Aktion, die nur "abc" ausgibt.
repeat ist eine Funktion, die den Wert x annimmt und eine unendliche Liste mit nur x zurückgibt.
sequence_ ist eine Funktion, die eine Liste von E / A-Aktionen aufnimmt und eine E / A-Aktion zurückgibt, die alle Aktionen nacheinander ausführt.
Im Grunde genommen erstellt dieses Programm eine unendliche Liste von Druckbefehlen "abc" und führt sie wiederholt aus. ohne Schleifen oder Rekursion.
quelle
repeat
wäre soa programming language statement which allows code to be repeatedly executed
.fix(print"">>)
Dies beinhaltet auch keine explizit genannten Wiederholungsfunktionen.ASM (x86 + I / O für Linux)
Es spielt keine Rolle, wie schwer Ihre mickrigen Hochsprachen werden, es wird immer noch nur eine versteckte Manipulation des Befehlszeigers sein. Am Ende wird es eine Art "goto" (jmp) sein, es sei denn, Sie sind gelangweilt genug, um die Schleife in der Laufzeit abzurollen.
Sie können Code auf Ideone testen
Sie können auch eine verfeinerte Version dieser Idee im Matteo Italia-DOS-Code nachlesen .
Es beginnt mit der Zeichenfolge 0..9 und ersetzt sie durch A..J, es werden keine direkten Sprünge verwendet (also sagen wir, es ist kein "goto" aufgetreten), auch keine Wiederholung.
Der Code könnte wahrscheinlich kleiner sein, wenn die Adressberechnung missbraucht wird, aber die Arbeit am Online-Compiler ist lästig, und ich werde ihn so lassen, wie er ist.
Kernteil:
Ganzer Code
quelle
C Präprozessor
Eine kleine "Technik", die ich mir bei einer Obfuscation Challenge ausgedacht habe. Es gibt keine Funktionsrekursion, aber es gibt ... Dateirekursion?
noloop.c:
Ich habe das mit gcc geschrieben / getestet. Offensichtlich muss Ihr Compiler das
__INCLUDE_LEVEL__
Makro (oder alternativ das__COUNTER__
Makro mit einigen Optimierungen) unterstützen, damit dies kompiliert werden kann. Es sollte ziemlich offensichtlich sein, wie dies funktioniert, aber zum Spaß sollten Sie den Präprozessor ausführen, ohne den Code zu kompilieren (verwenden Sie das-E
Flag mit gcc).quelle
PHP
Hier ist eine mit PHP. Führt eine Schleife durch, indem dieselbe Datei eingeschlossen wird, bis der Zähler $ max erreicht:
Das gleiche wie eine for-Schleife:
quelle
header("Location: .?x=".$_GET['x']+1);
als Rekursion gezählt?Python
Der folgende Code enthält keine rekursive Funktion (direkt oder indirekt), kein Schleifenprimitiv und ruft keine eingebaute Funktion auf (außer
print
):Druckt "Hallo Welt!" eine bestimmte Anzahl von Malen.
Erläuterung: Die Funktion
z
implementiert den strengen Z- Festkomma-Kombinator , mit dem (obwohl nicht rekursiv definiert) ein beliebiger rekursiver Algorithmus ausgedrückt werden kann.quelle
g
sehr indirekt rekursiv nennen.g
man dasg
nochmal? Natürlich ist der Trick die Selbstanwendungg(g)
, aber es gibt keine Rekursion. Würden Sie in der Tatg
indirekt rekursiv nennen, wenn Sie nicht gesehen habeng(g)
? Dies ist die Standardmethode für Sprachen, die keine rekursiven Definitionen zulassen, z. B. die Lambda-Rechnung.g
als Argumentx
und rufen dann anx(x)
.z80 Maschinencode
In einer Umgebung, in der Sie an jedem Adress- und Zuordnungs-ROM überall ausführen können, ordnen Sie dem gesamten Adressraum 64 KB ROM mit Nullen zu.
Was es macht: nichts. Wiederholt.
So funktioniert es: Der Prozessor beginnt mit der Ausführung, das Byte
00
ist einenop
Anweisung, daher fährt er einfach fort, erreicht die Adresse$ffff
, wechselt zu$0000
und führtnop
s weiter aus, bis Sie es zurücksetzen.Um es etwas interessanter zu machen, füllen Sie den Speicher mit einem anderen Wert (achten Sie darauf, Steuerflussanweisungen zu vermeiden).
quelle
Perl-Regex
Demo
oder versuchen Sie es als:
Das
(?!)
passt nie zusammen. Daher versucht die Regex-Engine, alle Positionen mit der Breite Null in der übereinstimmenden Zeichenfolge abzugleichen.Das
(q x x x 10)
ist das Gleiche wie(" " x 10)
- Wiederholen Sie diespace
zehn Mal.Bearbeiten: Die "Zeichen" wurden zur besseren Verständlichkeit in Positionen mit der Breite Null geändert . Hier finden Sie Antworten auf diese Stapelüberlauf-Frage .
quelle
T-SQL -12
quelle
GO
ist eigentlich eine SSMS-Direktive, weshalb Sie sie nicht in T-SQL-Skriptobjekte einfügen können, wie beispielsweise eine gespeicherte Prozedur.C #
Gibt alle Ganzzahlen von uint.MaxValue bis 0 aus.
quelle
JS (im Browser)
Wie wäre es damit?
Druckt die aktuelle Uhrzeit und lädt die Seite neu.
quelle
[Exception... "The operation is insecure." code: "18" nsresult: "0x80530012 (SecurityError)" location: "<unknown>"]