In vielen Sprachen wird die Syntax function_name(arg1, arg2, ...)
zum Aufrufen einer Funktion verwendet. Wenn wir die Funktion ohne Argumente aufrufen wollen, müssen wir tun function_name()
.
Ich finde es seltsam, dass ein Compiler oder ein ()
Skriptinterpreter dies als Funktionsaufruf erfolgreich erkennen müsste . Wenn bekannt ist, dass eine Variable aufrufbar ist, warum würde das nicht function_name;
ausreichen?
Auf der anderen Seite können wir in einigen Sprachen: function_name 'test';
oder sogar function_name 'first' 'second';
eine Funktion oder einen Befehl aufrufen.
Ich denke, Klammern wären besser gewesen, wenn sie nur zur Angabe der Prioritätsreihenfolge benötigt worden wären, und an anderen Stellen wären sie optional. Beispielsweise sollte doing if expression == true function_name;
so gültig sein wie if (expression == true) function_name();
.
Das Ärgerlichste ist meiner Meinung nach, 'SOME_STRING'.toLowerCase()
wenn die Prototypfunktion eindeutig keine Argumente benötigt. Warum haben sich die Designer gegen das Einfachere entschieden 'SOME_STRING'.lower
?
Haftungsausschluss: Verstehen Sie mich nicht falsch, ich liebe die C-ähnliche Syntax! ;) Ich frage nur, ob es besser sein könnte. Hat das Erfordernis ()
Leistungsvorteile oder erleichtert es das Verständnis des Codes? Ich bin sehr gespannt, was genau der Grund ist.
quelle
()
, aber das, was in Ihrem Beitrag auffällt, ist dieif (expression == true)
Aussage. Sie sorgen sich um überflüssige()
, aber dann verwenden Sie eine überflüssige== true
:)Antworten:
Für Sprachen, die erstklassige Funktionen verwenden , ist es durchaus üblich, dass die Syntax für den Verweis auf eine Funktion wie folgt lautet:
während der Akt des Aufrufs dieser Funktion ist:
a
Im obigen Beispiel würde auf die obige Funktion verwiesen (und Sie könnten sie durch Ausführen aufrufena()
), währendb
der Rückgabewert der Funktion enthalten würde.Während einige Sprachen Funktionsaufrufe ohne Klammern ausführen können, kann es verwirrend werden, ob sie die Funktion aufrufen oder einfach auf die Funktion verweisen .
quelle
make_list
, die ihre Argumente in eine Liste packt, gibt es einen großen Unterschied zwischen derf(make_list)
Übergabe einer Funktion anf
eine leere Liste.SATInstance.solve
eine unglaublich teure reine Funktion an eine andere Funktion zu übergeben, ohne sofort zu versuchen, sie auszuführen. Es macht die Dinge auch sehr umständlich, wennsomeFunction
etwas anders ist, je nachdem, obsomeFunction
es als rein deklariert wird. Zum Beispiel, wenn ich (Pseudo - Code) habenfunc f() {return g}
,func g() {doSomethingImpure}
undfunc apply(x) {x()}
dannapply(f)
ruftf
. Wenn ich dann erklären , dassf
rein ist , dann plötzlichapply(f)
gehtg
aufapply
, undapply
Anrufeg
und tut etwas unrein.apply(f)
, wenng
es unrein ist (undapply
auch?), Dann ist das Ganze unrein und bricht natürlich zusammen.foo.bar()
nicht als eine Operation "bar
Objektmethode aufrufenfoo
", sondern als zwei Operationen "bar
Objektfeld dereferenzierenfoo
und dann das von diesem Feld zurückgegebene Objekt aufrufen" angegeben. Das.
ist also der Feldauswahl-Operator und()
der Anruf-Operator, und sie sind unabhängig.In der Tat erlaubt Scala dies, obwohl es eine Konvention gibt, die befolgt wird: Wenn die Methode Nebenwirkungen hat, sollten trotzdem Klammern verwendet werden.
Als Compiler-Autor empfinde ich das garantierte Vorhandensein von Klammern als recht praktisch. Ich würde immer wissen, dass dies ein Methodenaufruf ist, und ich müsste für den einen oder anderen Fall keine Gabelung einbauen.
Als Programmierer und Codeleser lässt das Vorhandensein von Klammern keinen Zweifel daran, dass es sich um einen Methodenaufruf handelt, obwohl keine Parameter übergeben werden.
Die Übergabe von Parametern ist nicht das einzige bestimmende Merkmal eines Methodenaufrufs. Warum sollte ich eine parameterlose Methode anders behandeln als eine Methode mit Parametern?
quelle
def foo()
also eine Methode mit einer leeren Parameterliste unddef foo
ist eine Methode ohne Parameterliste und die beiden sind verschiedene Dinge ! Im Allgemeinen muss eine Methode ohne Parameterliste ohne Argumentliste und eine Methode mit leerer Parameterliste mit leerer Argumentliste aufgerufen werden. Das Aufrufen einer Methode ohne Parameterliste mit einem leeren Argument ist tatsächlich…apply
Methode des vom Methodenaufruf zurückgegebenen Objekts mit einer leeren Argumentliste interpretiert werden , während der Aufruf einer Methode mit einer leeren Parameterliste ohne eine Argumentliste je nach Kontext unterschiedlich als Aufruf der Methode mit einer leeren Argumentliste interpretiert werden kann, η-Erweiterung zu einer teilweise angewendeten Methode (dh "Methodenreferenz"), oder es wird überhaupt nicht kompiliert. Außerdem folgt Scala dem Prinzip des einheitlichen Zugriffs, indem nicht (syntaktisch) unterschieden wird, ob eine Methode ohne Argumentliste aufgerufen wird oder auf ein Feld verwiesen wird.Dies ist eigentlich ein ziemlich subtiler Zufall bei der Syntaxauswahl. Ich spreche mit funktionalen Sprachen, die auf der typisierten Lambda-Rechnung basieren.
In diesen Sprachen hat jede Funktion genau ein Argument . Was wir oft als "Mehrfachargumente" betrachten, ist eigentlich ein einziger Parameter des Produkttyps. So zum Beispiel die Funktion, die zwei ganze Zahlen vergleicht:
nimmt ein einzelnes Paar von ganzen Zahlen. Die Klammern bezeichnen nicht die Funktionsparameter. Sie werden verwendet, um das Argument mit einem Muster abzugleichen. Um Sie davon zu überzeugen, dass dies wirklich ein Argument ist, können wir statt der Mustererkennung die projektiven Funktionen verwenden, um das Paar zu dekonstruieren:
Somit sind die Klammern wirklich eine Annehmlichkeit, die es uns ermöglicht, Musterabgleiche vorzunehmen und einige Eingaben zu sparen. Sie sind nicht erforderlich.
Was ist mit einer Funktion, die angeblich keine Argumente hat? Eine solche Funktion hat eigentlich Domain
Unit
. Das einzelne Mitglied vonUnit
wird normalerweise als geschrieben()
, daher werden die Klammern angezeigt.Um diese Funktion zu nennen, würden wir es auf einen Wert von Typ gelten müssen
Unit
, die sein müssen()
, so dass wir am Ende schreibensay_hi ()
.Es gibt also wirklich keine Argumentliste!
quelle
()
ähneln leere Klammern Löffeln ohne den Griff.leq
Funktion, die<
eher verwendet als<=
verwirrend ist!()
zur Darstellung der Einheit vertraut . Es würde mich nicht wundern, wenn zumindest ein Teil des Grundes darin bestünde, einer "parameterlosen Funktion" zu ähneln. Es gibt jedoch eine andere mögliche Erklärung für die Syntax. In der Algebra der TypenUnit
ist in der Tat die Identität für Produkttypen. Ein binäres Produkt wird geschrieben als(a,b)
, wir könnten ein unäres Produkt schreiben als(a)
, also wäre eine logische Erweiterung, das nulläre Produkt als einfach zu schreiben()
.Wenn Sie in Javascript beispielsweise einen Methodennamen ohne () verwenden, wird die Funktion selbst zurückgegeben, ohne sie auszuführen. Auf diese Weise können Sie beispielsweise die Funktion als Argument an eine andere Methode übergeben.
In Java wurde die Wahl getroffen, dass ein Bezeichner gefolgt von () oder (...) einen Methodenaufruf bedeutet, während ein Bezeichner ohne () auf eine Mitgliedsvariable verweist. Dies kann die Lesbarkeit verbessern, da Sie keinen Zweifel haben, ob Sie es mit einer Methode oder einer Membervariablen zu tun haben. Tatsächlich kann derselbe Name sowohl für eine Methode als auch für eine Membervariable verwendet werden. Beide sind mit ihrer jeweiligen Syntax zugänglich.
quelle
Perl Best Practices
tutObject::toString
identifiziert eine Methode.Die Syntax folgt der Semantik. Beginnen wir also mit der Semantik:
Tatsächlich gibt es mehrere Möglichkeiten:
Eine ähnliche Syntax für verschiedene Verwendungszwecke ist der beste Weg, um entweder eine mehrdeutige oder zumindest eine verwirrende Sprache zu erstellen (und wir haben genug davon).
In C und C-ähnlichen Sprachen:
func()
&func
(C Erstellen eines Funktionszeigers) erfolgen.someVariable::someMethod
zum Beispiel (beschränkt auf die Methode Empfänger, aber immer noch nützlich)Beachten Sie, dass jede Verwendung eine andere Syntax aufweist, sodass Sie sie leicht voneinander unterscheiden können.
quelle
::
Operator in Java ist ein Teilanwendungsoperator, kein laufender Operator. Teilanwendung ist eine Operation, die eine Funktion und einen oder mehrere Werte übernimmt und eine Funktion zurückgibt, die weniger Werte akzeptiert. Currying funktioniert nur mit Funktionen, nicht mit Werten.In einer Sprache mit Nebenwirkungen ist es IMO sehr hilfreich, zwischen dem (theoretisch nebenwirkungsfreien) Lesen einer Variablen zu unterscheiden
und Aufrufen einer Funktion, die Nebenwirkungen verursachen kann
OTOH, wenn es keine Nebenwirkungen gibt (außer denen, die im Typsystem kodiert sind), ist es sinnlos, zwischen dem Lesen einer Variablen und dem Aufrufen einer Funktion ohne Argumente zu unterscheiden.
quelle
noun.verb
Die Syntax könnte eine so klare Bedeutung haben wienoun.verb()
und etwas weniger überladen sein. In ähnlicher Weise eine Funktionmember_
könnte bietet eine freundliche Syntax als eine typische Set - Funktion eine linke Referenz Rückkehr:obj.member_ = new_value
gegenobj.set_member(new_value)
(die_
Postfix ist ein Hinweis sagen „ausgesetzt“ erinnert an_
Präfixen für „versteckte“ Funktionen).noun.verb
.Keine der anderen Antworten hat versucht, die Frage zu beantworten: Wie viel Redundanz sollte das Design einer Sprache haben? Denn selbst wenn Sie eine Sprache entwerfen können, bei der
x = sqrt y
x auf die Quadratwurzel von y gesetzt wird, bedeutet dies nicht, dass Sie dies unbedingt tun sollten.In einer Sprache ohne Redundanz bedeutet jede Zeichenfolge etwas, was bedeutet, dass bei einem einzelnen Fehler keine Fehlermeldung angezeigt wird und Ihr Programm das Falsche tut, was möglicherweise etwas ganz anderes ist als das, was Sie tun beabsichtigt und sehr schwer zu debuggen. (Jeder, der mit regulären Ausdrücken gearbeitet hat, wird es wissen.) Ein wenig Redundanz ist eine gute Sache, da so viele Ihrer Fehler erkannt werden können. Je mehr Redundanz vorhanden ist, desto wahrscheinlicher ist es, dass die Diagnose korrekt ist. Jetzt können Sie das natürlich zu weit bringen (keiner von uns möchte heutzutage in COBOL schreiben), aber es gibt eine Balance, die richtig ist.
Redundanz hilft auch bei der Lesbarkeit, da es mehr Hinweise gibt. Englisch ohne Redundanz wäre sehr schwer zu lesen, und das Gleiche gilt für Programmiersprachen.
quelle
:=
und Vergleich==
mit=
nur verwendbar ist für Kompilierung-Initialisierung, dann ist die Wahrscheinlichkeit , dass Fehler Vergleiche in Zuweisungen drehen würde stark vermindert.()
Aufruf einer Nullargumentfunktion erfordern, aber auch ein Token, um die Adresse einer Funktion zu übernehmen. Die erstere Aktion ist viel häufiger, daher ist das Speichern eines Tokens im selteneren Fall nicht allzu nützlich.In den meisten Fällen handelt es sich hierbei um syntaktische Auswahlmöglichkeiten der Grammatik der Sprache. Für die Grammatik ist es nützlich, wenn die verschiedenen Einzelkonstrukte (relativ) eindeutig sind, wenn sie alle zusammen genommen werden. (Wenn es Unklarheiten gibt, wie in einigen C ++ - Deklarationen, müssen bestimmte Regeln für die Auflösung festgelegt werden.) Der Compiler kann nicht raten. Es ist erforderlich, die Sprachspezifikation zu befolgen.
Visual Basic unterscheidet in verschiedenen Formen zwischen Prozeduren, die keinen Wert zurückgeben, und Funktionen.
Prozeduren müssen als Anweisung aufgerufen werden und erfordern keine Parens, sondern nur durch Kommas getrennte Argumente, falls vorhanden. Funktionen müssen als Teil eines Ausdrucks aufgerufen werden und erfordern die Parens.
Es ist eine relativ unnötige Unterscheidung, die das manuelle Umgestalten zwischen den beiden Formen schmerzhafter macht, als es sein muss.
(Auf der anderen Seite Visual Basic verwendet die gleichen Pars
()
für Array - Referenzen wie für Funktionsaufrufe, so Referenzen Array aussehen Funktion aufruft. Und das erleichtert manuelles Refactoring eines Arrays in einen Funktionsaufruf! So konnten wir darüber nachdenken andere Sprachen verwenden[]
‚s für Array-Referenzen, aber ich schweife ab ...)In den Sprachen C und C ++ wird auf den Inhalt von Variablen automatisch unter Verwendung ihres Namens zugegriffen. Wenn Sie auf die Variable selbst anstelle ihres Inhalts verweisen möchten, wenden Sie den unären
&
Operator an.Diese Art von Mechanismus könnte auch auf Funktionsnamen angewendet werden. Der rohe Funktionsname könnte einen Funktionsaufruf implizieren, während ein unärer
&
Operator verwendet wird, um die Funktion (selbst) als Daten zu bezeichnen. Persönlich mag ich die Idee, auf nebenwirkungsfreie Funktionen ohne Argumente mit der gleichen Syntax wie Variablen zuzugreifen.Dies ist durchaus plausibel (wie auch andere syntaktische Möglichkeiten hierfür).
quelle
call <procedure name> (<arguments>)
? Ich bin mir ziemlich sicher, dass dies eine gültige Methode war, um Prozeduren zu verwenden, als ich in einem anderen Leben QuickBASIC-Programmierung durchgeführt habe ...Call
eine Methode verwenden muss, die ich in "normalem" Produktionscode fast nie verwenden muss.Konsistenz und Lesbarkeit.
Wenn ich erfahren , dass Sie die Funktion aufrufen
X
wie folgt aus :X(arg1, arg2, ...)
, dann erwarte ich , dass es die gleiche Art und Weise für keine Argumente arbeiten:X()
.Gleichzeitig lerne ich, dass Sie die Variable als ein Symbol definieren und wie folgt verwenden können:
Was würde ich jetzt denken, wenn ich das finde?
Deine Vermutung ist genauso gut wie meine. Unter normalen Umständen ist das
X
eine Variable. Aber wenn wir Ihren Weg gehen würden, könnte es auch eine Funktion sein! Muss ich mir merken, ob welches Symbol welcher Gruppe (Variablen / Funktionen) zugeordnet ist?Wir könnten künstliche Einschränkungen auferlegen, z. B. "Funktionen beginnen mit Großbuchstaben. Variablen beginnen mit Kleinbuchstaben". Dies ist jedoch nicht erforderlich und kompliziert die Dinge, obwohl es manchmal hilfreich sein kann, einige Entwurfsziele zu erreichen.
Randnotiz 1: Bei anderen Antworten wird eines völlig ignoriert: Mit der Sprache können Sie möglicherweise denselben Namen für Funktion und Variable verwenden und nach Kontext unterscheiden. Siehe
Common Lisp
als Beispiel. Funktionx
und Variablex
koexistieren perfekt.Side-Anmerkung 2: Die akzeptierte Antwort zeigt uns die Syntax:
object.functionname
. Erstens ist es nicht universell für Sprachen mit erstklassigen Funktionen. Zweitens würde ich als Programmierer dies als zusätzliche Information behandeln:functionname
gehört zu einemobject
. Obobject
es sich um ein Objekt, eine Klasse oder einen Namespace handelt, spielt keine Rolle, aber es sagt mir, dass es irgendwo hingehört . Dies bedeutet, dass Sie entwederobject.
für jede globale Funktion eine künstliche Syntax hinzufügen oder eineobject
für alle globalen Funktionen erstellen müssen.In beiden Fällen verlieren Sie die Möglichkeit, separate Namespaces für Funktionen und Variablen zu haben.
quelle
Nehmen Sie zum Hinzufügen zu den anderen Antworten das folgende C-Beispiel:
Wenn der Aufrufoperator optional wäre, gäbe es keine Möglichkeit, zwischen der Funktionszuweisung und dem Funktionsaufruf zu unterscheiden.
Dies ist noch problematischer in Sprachen ohne Typensystem, z. B. JavaScript, in denen die Typinferenz nicht verwendet werden kann, um herauszufinden, was eine Funktion ist und was nicht.
quelle
function cross(a, b) { return a + b; }