Schreiben Sie eine Funktion (oder ein Makro), die genau dann true zurückgibt, wenn mindestens eines ihrer Argumente einen Aufruf der Funktion selbst enthält, andernfalls false .
Zum Beispiel:
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)
BEARBEITEN: Die Funktion / das Makro kann andere Hilfsfunktionen / Makros aufrufen.
EDIT 2: Die Funktion muss mindestens ein Argument annehmen, es sei denn, die verwendete Sprache verhält sich wie C, wobei eine Funktion, die keine Argumente akzeptiert, dennoch mit Argumenten aufgerufen werden kann.
print(func(), func(func()))
oder wird die Funktion erst direkt nach ihrer Definition auf oberster Ebene aufgerufen?Antworten:
Mathematica ...
... wurde dafür gemacht .
Alles ist ein Ausdruck, und ein Ausdruck hat einen Kopf und eine Reihe von Elementen. So
1+2
ist es tatsächlichPlus[1,2]
und{1,2}
ist es tatsächlichList[1,2]
. Dies bedeutet, dass wir jedem Ausdruck für den Kopf, an dem wir interessiert sind, entsprechen können - in diesem Fall der Funktionf
selbst.Alles, was wir tun müssen, ist einen Weg zu finden, um zu verhindern, dass Mathematica die Funktionsargumente auswertet, bevor die Funktion aufgerufen wird, sodass wir den Ausdrucksbaum innerhalb der Funktion analysieren können. Was ist was
Hold
und wofürHoldAll
. Mathematica selbst verwendet dies überall, um Sonderfälle für bestimmte Implementierungen bereitzustellen. Wenn SieLength[Permutations[list]]
es beispielsweise aufrufen , werden niemals all diese Permutationen erstellt und viel Speicher verschwendet, sondern es wird stattdessen erkannt, dass es dies einfach als berechnen kannLength[list]!
.Schauen wir uns den obigen Code am
f[True || f[1]]
Beispiel des letzten Aufrufs genauer an. Normalerweise wertet Mathematica zuerst Funktionsargumente aus, sodass dies einfach kurzgeschlossen und aufgerufen wirdf[True]
. Wir haben uns jedoch eingestelltDies weist Mathematica an, die Argumente nicht auszuwerten, so dass der
FullForm
Aufruf (dh der interne Ausdrucksbaum ohne syntaktischen Zucker) lautetUnd das Argument wird tatsächlich
f
in dieser Form empfangen . Das nächste Problem ist, dassf
Mathematica innerhalb von , sobald wir das Argument verwenden, erneut versuchen wird, es zu bewerten. Wir können dies lokal mitHold@x
(syntaktischer Zucker fürHold[x]
) unterdrücken . An diesem Punkt haben wir den ursprünglichen Ausdrucksbaum im Griff und machen damit, was wir wollen.Um nach einem Muster in einem Ausdrucksbaum zu suchen, können wir verwenden
FreeQ
. Es wird überprüft, ob ein bestimmtes Muster nicht im Ausdrucksbaum gefunden wird. Wir verwenden das Muster_f
, das jedem Unterausdruck mit dem Kopf entsprichtf
(genau das, wonach wir suchen). Gibt natürlichFreeQ
das Gegenteil von dem zurück, was wir wollen, also negieren wir das Ergebnis.Noch eine Subtilität: Ich habe das Argument als
x___
eine Folge von 0 oder mehr Elementen angegeben. Dies stellt sicher, dassf
mit einer beliebigen Anzahl von Argumenten funktioniert. Beim ersten Aufruff[]
bedeutet dies, dassHold@x
einfach wirdHold[]
. Wenn es mehrere Argumente gäbef[0,f[1]]
,Hold@x
wäre das soHold[0,f[1]]
.Das ist wirklich alles.
quelle
C ++ 11
Ähnlich wie bei Ausdrucksvorlagen können wir die Tatsache verbreiten, dass wir die Funktion in einer Funktionsparameterliste in ihrem Rückgabetyp aufgerufen haben.
Zuerst brauchen wir einige Hilfsklassen und Funktionen:
Jetzt können wir es für genau den gleichen Code wie in der Frage verwenden:
Ausgabe von ideone:
quelle
C #
Verwendung (in LINQPad ):
Der Trick dabei ist,
f
die Parameter besser bekannt zu machen , indem ein benutzerdefinierter Typ (PcgBool
) übergeben wird, der einem Booleschen Wert ähnelt.PS Ich hoffe, dass die Rückgabe eines benutzerdefinierten Typs, der implizit von und zu einem Bool umgewandelt werden kann, nicht als Betrug angesehen wird. Technisch gesehen können Sie den Rückgabewert so verwenden, als wäre er vom Typ bool, und die Frage lautet "return true if and only if" usw., ohne jedoch anzugeben, dass der Rückgabetyp bool sein muss.
quelle
f(new PcgBool())
?Lua
quelle
C ++ 11 TMP
Dieser ist etwas länger. Aufgrund einiger Einschränkungen in C ++ - Vorlagen musste ich alles mit Typen machen. So wurden "True" sowie die Zahlen in bool und int umgewandelt. Auch die Operationen +, - und || wurden in add, mul und or_ umgewandelt.
Ich hoffe, dass dies immer noch eine gültige Antwort ist.
quelle
is_given_foo
der ersten vorgezogen wird?C.
Ich denke nicht, dass dies mit Prozeduren möglich ist, aber wir können immer (ab) Makros verwenden.
Dies gibt aus:
Unser Makro
f
verfolgt die aktuelle Tiefe und die maximale Tiefe, die seit dem Aufruf erreicht wurde. Wenn das letztere größer als das erstere ist, wurde es rekursiv aufgerufen.quelle
&&
und gehackt habe||
. Ich kann versuchen, meine Antwort einzulösen.C, 13
Das Argument wird nie erweitert, daher können keine Makros aufgerufen werden. Es ist nichts weiter als ein Haufen Klartext. Somit ist die Antwort immer falsch.
quelle
F(F(0))
Ich werde gerne zuerst das Argument von F bewertenF(0)
. Dieses Argument wird auf 0 erweitert. Dann wertet es F ohne blaue Farbe für sein Argument 0 aus, was zu 0 führt. Die nicht rekursive Einschränkung gilt nicht. das ist, wenn ich zB habe#define F(X) G
und#define G F(Y)
im Spiel bin ; In diesem Fall wird der Token angezeigt , während F (0) auf G und dann auf F (Y) erweitert wirdF
. Da ich gerade F erweitere, hat F in diesem Fall blaue Farbe und daher stoppt die Erweiterung bei F (Y).X
die daran gebundenen Makros niemals erweitert werden , da sie nicht in der Liste der Argumentersetzungen enthalten sind. Wenn wir das als eine aufgerufene Funktion interpretieren, bedeutet dies, dass die Funktionen des Arguments niemals aufgerufen werden. Ja, ich denke das ist richtig.C.
Wir können die Rekursionstiefe im Makro zählen. Wir überschreiben dann den Rückgabewert des äußeren Makros im inneren Makro.
!!b
ist es, den Rückgabewert auf einen Booleschen Wert zu normalisieren. Der vorverarbeitete Code endet wie folgt:quelle
printf("%d\n", foo(printf("%d\n", foo(1))))
. Der innere Aufruf vonfoo
gibt 1 zurück, ruft aber nicht anfoo
.C.
Das Makro vergleicht, ob sein Argument mit "BLAH (" beginnt).
quelle
BLAH(blub(BLAH(0)))
.Algol 60
Hier ist eine Funktion
boolean procedure
, die genau das tut, wonach die Frage verlangt (Hinweis: Algol 60 wird anhand einer Liste von Token definiert, ohne dass die Syntax für diese festgelegt wird. Im Folgenden wird die Marst-Syntax verwendet, um die einzelnen Token darzustellen, aus denen das Programm besteht):Nachprüfung
Hier ist der Testcode, den ich verwendet habe:
Wie erwartet ist die Ausgabe:
Erläuterung
Algol 60 hat eine andere Bewertungsreihenfolge als die meisten Sprachen, die eine eigene Logik hat und tatsächlich viel leistungsfähiger und allgemeiner als die typische Bewertungsreihenfolge ist, aber für Menschen ziemlich schwer zu verstehen ist (und auch ziemlich schwer für Computer effizient zu implementieren, weshalb es für Algol geändert wurde 68). Dies ermöglicht eine Lösung ohne Betrug (das Programm muss nicht auf den Analysebaum oder ähnliches schauen, und im Gegensatz zu fast allen anderen Lösungen hier würde dies gut funktionieren, wenn der verschachtelte Aufruf über eine erfolgt wäre FFI).
Ich beschloss auch, ein paar andere Macken der Sprache zu zeigen. (Insbesondere können Variablennamen Leerzeichen enthalten. Dies ist für die Lesbarkeit ziemlich nützlich, da sie keine Unterstriche enthalten können. Ich mag auch die Tatsache, dass der Kommentarindikator das wörtliche Wort
comment
in den meisten Syntaxcodierungen ist. Algol 68 fand dies kurz gesagt ziemlich umständlich Kommentare und¢
als Alternative eingeführt. Die Anführungszeichen um den Kommentarkörper werden normalerweise nicht benötigt. Ich füge sie nur hinzu, um Klarheit zu schaffen und um zu verhindern, dass der Kommentar versehentlich endet, wenn ich ein Semikolon eingebe.) Ich mag die allgemeinen Konzepte der Sprache wirklich sehr (wenn nicht die Details), aber es ist so ausführlich, dass ich es selten auf PPCG verwenden kann.Algol 60 unterscheidet sich hauptsächlich von den Sprachen, die es inspiriert hat (wie Algol 68 und indirekt C, Java usw .; Leute, die K & R C kennen, werden diese Syntax für Funktionen wahrscheinlich erkennen), dass ein Funktionsargument ein bisschen wie behandelt wird ein kleines Lambda für sich; Wenn Sie beispielsweise das Argument
5
einer Funktion geben, die nur die Zahl 5 ist, aber wenn Sie das Argumentx+1
angeben, erhalten Sie genau das, was Sie angegeben haben, das Konzept von "x
plus 1" und nicht das Ergebnis vonx
plus 1. Der Unterschied hier Wennx
Änderungen vorgenommen werden, wird beim Versuch, das betreffende Funktionsargument auszuwerten, der neue Wert von angezeigtx
. Wenn ein Funktionsargument nicht innerhalb der Funktion ausgewertet wird, wird es auch nicht außerhalb der Funktion ausgewertet. Wenn es innerhalb der Funktion mehrmals ausgewertet wird, wird es jedes Mal separat ausgewertet (vorausgesetzt, dies kann nicht optimiert werden). Dies bedeutet, dass es möglich ist, beispielsweise die Funktionalität vonif
oderwhile
in einer Funktion zu erfassen .In diesem Programm nutzen wir die Tatsache, dass, wenn ein Aufruf einer Funktion in einem Argument für diese Funktion erscheint, die Funktion rekursiv ausgeführt wird (da das Argument genau an dem Punkt oder den Punkten ausgewertet wird, an denen die Funktion es auswertet nicht früher oder später, und dies muss notwendigerweise innerhalb des Funktionskörpers sein). Dies reduziert das Problem auf das Erkennen, ob die Funktion rekursiv ausgeführt wird, was viel einfacher ist. Alles, was Sie benötigen, ist eine threadlokale Variable, die erkennt, ob ein rekursiver Aufruf vorliegt (und in diesem Fall eine weitere, um Informationen in die andere Richtung zu übertragen). Wir können eine statische Variable verwenden (dh
own
) zu diesem Zweck, da Algol 60 Single-Threaded ist. Danach müssen wir nur noch alles so zurücksetzen, wie es war, damit die Funktion bei mehrmaligem Aufruf korrekt funktioniert (gemäß den PPCG-Regeln).Die Funktion gibt im Moment nicht den gewünschten Wert aus den internen Aufrufen zurück (zumindest wenn Sie davon ausgehen, dass sie nur in ihren Argumenten nach Selbstaufrufen suchen sollten , anstatt sich selbst zu zählen). Diese Arbeit zu machen ist ziemlich einfach, wenn man dieselben allgemeinen Prinzipien verwendet, aber komplexer und würde die Funktionsweise der Funktion verdecken. Wenn dies zur Beantwortung der Frage als notwendig erachtet wird, sollte es nicht zu schwierig sein, dies zu ändern.
quelle
Java
kann
reset()
als Hilfsmittel gezählt werden?Ausgabe:
BEARBEITEN
Hier ist eine andere Version, die diese
reset()
Methode nicht verwendet , aber viel Wahnsinn. Es erstellt und kompiliert zur Laufzeit den obigen Code mit dem Aufruf der Funktion, die als Argument in stdin übergeben wird. Ich wollte eine elegantere Lösung, aber leider habe ich nicht zu viel Zeit dafür :(Um es auszuführen, rufen Sie einfach zum Beispiel auf
javac FuncOFunc.java function(function(1),function(),4)
.quelle
reset()
nach jedemfunction
Aufruf aufzurufen . Die Idee der Hilfsfunktionen besteht darin, dass Sie andere private Methoden ausfunction
dem Körper heraus aufrufen können ... Aber das ist nur meine Interpretation der Frage. Überlassen wir die Entscheidung dem Fragesteller.reset()
weniger Version. Der obige Code funktioniert immer noch, wenn es nur einen Aufruf in der Hauptleitung gibt (ohne die Anzahl der Variablen und die Rücksetzfunktion)Python
Speichern Sie es als
selfarg.py
und führen Sie es oderfrom selfarg import function
in einem anderen Skript aus. Es funktioniert nicht in der Antwort.Bei Verwendung des aktuellen und des äußeren Stapelrahmens werden
function
Name und Ort des Aufrufs (Datei- und Zeilennummer) ermittelt. Anschließend wird die erhaltene Datei geöffnet und in einen abstrakten Syntaxbaum analysiert. Es springt zu dem durch die Zeilennummer gekennzeichneten Funktionsaufruf und prüft, ob seine Argumente einen anderen Funktionsaufruf mit demselben Namen enthalten.edit : Auch mit python2 ist es in Ordnung. Python3 wurde im Titel in Python geändert.
quelle