Ich schreibe über Sprachsyntax. Gibt es eine Sprache, in der Parameter innerhalb des Methodennamens stehen?

29

in JavaScript:

function getTopCustomersOfTheYear(howManyCustomers, whichYear) {
   // Some code here.
}
getTopCustomersOfTheYear(50, 2010);

in C #:

public List<Customer> GetTopCustomersOfTheYear(int howManyCustomers, 
 int whichYear)
{
   // Some code here
}
List<Customer> customers = GetTopCustomersOfTheYear(50, 2010);

in PHP:

public function getTopCustomersOfTheYear($howManyCustomers, $whichYear)
{
   // Some code here
}
$customers = getTopCustomersOfTheYear(50, 2010);

Gibt es eine Sprache, die diese Syntax unterstützt:

function GetTop(x)CustomersOfTheYear(y)
{
    // Some code here
}
returnValue = GetTop(50)CustomersOfTheYear(2010);

Ist es nicht eine semantischere, lesbarere Form, eine Funktion zu schreiben?

Update: Der Grund für diese Frage ist, dass ich einen Artikel über eine neue Syntax für eine neue Sprache schreibe. Ich dachte jedoch, dass eine solche Syntax zum Deklarieren von Methoden für Entwickler angenehmer und benutzerfreundlicher sein könnte und die Lernkurve der Sprache verringern würde, da sie der natürlichen Sprache näher kommt. Ich wollte nur wissen, ob diese Funktion bereits in Betracht gezogen wurde oder nicht.

Saeed Neamati
quelle
12
Es ist vielleicht einfacher zu lesen, wenn man sich erst einmal an die Redewendung gewöhnt hat, aber es scheint mir schwierig zu sein, einen korrekten Parser dafür zu schreiben.
Mason Wheeler
2
Was meinen Sie mit "Platzhaltern"? Diese Frage ist schwer zu verstehen, obwohl das letzte Codebeispiel an Objective-C und SmallTalk erinnert.
greyfade
3
AFAIK Smalltalk war die erste Sprache, die diese Syntax ausschließlich wie 'hello world' indexOf: $o startingAt: 6oder verwendete Rectangle width: 100 height: 200. Übrigens, was ist mit dieser Frage los?
Maaartinus
8
Wenn du eine Klammer verschiebst, bekommst du: returnValue = GetTop(50, CustomersOfTheYear(2010))die sieht für mich gleich gut lesbar und eigentlich flexibler / orthogonaler aus. ... und ja, es ist ganz normale Syntax.
dagnelies
2
@arnaud: Da stimme ich voll zu. Die vorgeschlagene Syntax ist nur eine Problemumgehung für die fehlende Zerlegung.
back2dos

Antworten:

41

Ja und ja. Ja, es gibt eine solche Sprache, und viele Leute finden sie besser lesbar, wenn sie sich daran gewöhnt haben.

In Objective-C wäre die Methode:

- (NSArray*)getTop:(int)count customersOfTheYear:(Year)year;

Das ist eigentlich ein ziemlich ausgeklügeltes Beispiel, das nicht sehr gut liest. Deshalb hier ein besseres Beispiel aus aktuellem Code:

+ (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha;

Dies ist der Prototyp einer Methode, die eine neue UIColor-Instanz mit den Werten Rot, Grün, Blau und Alpha zurückgibt. Sie würden es so nennen:

UIColor *violet = [UIColor colorWithRed:0.8 green:0.0 blue:0.7 alpha:1.0];

Weitere Informationen zu Nachrichtennamen mit eingestreuten Parametern finden Sie in der Objective-C-Programmiersprache .

Caleb
quelle
16
Als ich das erste Mal in Objective CI darüber las, fand ich es klug, aber die genannten Parameter waren steifer und schwerer zu handhaben. Ansonsten gute Antwort.
ZJR
2
Ein Beispiel, wie die Methode aufgerufen wird, würde nicht schaden.
Greyfade
3
@ZJR, trotz der gegenteiligen Meinung von Wikipedia , ist Obj- Cs Stil wie benannte Parameter, aber anders. In dem obigen Beispiel die Methode ist Name: +colorWithRed:blue:green:alpha:. Ich war zufällig Namen für die Parameter zu verwenden, die die Methode übereinstimmen, aber ich konnte so leicht verwendet habe r, b, g, und a. Bei benannten Parametern wie JavaScript-Angeboten würden Sie die tatsächlichen Namen verwenden, um auf die Werte zuzugreifen. True Names-Parameter können normalerweise auch in beliebiger Reihenfolge angegeben werden, und Parameter können optional sein. So ähnlich, ja, aber grundlegend anders.
Caleb
2
Ich dachte an Python: color(r=0xFF,g=0xFF,b=0x55)oder color(b=0x32,r=0x7F,a=.5,g=0xFF)so. Nennen wir diese sehr benannten Parameter . :)
ZJR
3
@ZJR, Ihr Python-Beispiel ist richtig - genau das würde ich "named parameters" nennen. Ich sage nur, dass Obj-C dir das überhaupt nicht gibt. Die Syntax sieht viel wie die JavaScript benannte Parameter , da die Methode in Teile zerbrochen ist, und jeder Teil hat einen Doppelpunkt, aber es ist wirklich nicht , dass überhaupt . Die Namen in Obj-C sind Teil des Methodennamens und keine Namen, die verwendet werden können, um auf die Parameter in der Implementierung zu verweisen. Ordnung ist wichtig; Parameter sind nicht optional. Ich vermute, wir sind uns wahrscheinlich mehr als einig.
Caleb
25

Antwort: Smalltalk

http://en.wikipedia.org/wiki/Smalltalk

'hello world' indexOf: $o startingAt: 6 ist wie bei Java "hello world".indexOfStartingAt(o, 6)

Rectangle width: 100 height: 200 ist wie bei Java new Rectangle(100, 200)

Die Syntax lautet ... expression word1: parm1 word2: parm2 word3: parm3 ...Der Name der aufgerufenen Methode ist die Verkettung aller Wörter.

Jeff Grigg
quelle
17

Ich glaube, Sie suchen nach der Abstraktion " Fluent Interface" (ich möchte hiermit einen Kommentar, der ursprünglich von @Jesper verfasst wurde, zu einer "Antwort" machen). Dieses jetzt übliche Muster wurde erfolgreich in vielen Sprachen implementiert, von denen Objective-C nur eine ist.

Hier ist ein ziemlich sauberes Beispiel:

Person bo = new Person();
bo.Set.FirstName("Bo").LastName("Peep").Age(16).Size("Little").LostHerSheep();

Sie können sehen , wie so etwas kann in realisiert werden Randy Patterson ‚s Wie eine fließend Schnittstelle zu entwerfen .

Andre Vianna gibt einen kurzen Überblick und erläutert mögliche Implementierungen in zwei weiteren Teilen des Artikels, einschließlich zahlreicher nützlicher Informationen. Vianna verweist auf die alte Idee, die ich zum ersten Mal in Smalltalk 80 mit der Bezeichnung " Cascading " ( Kaskadierung ) kennengelernt habe und die das Senden mehrerer Nachrichten an dasselbe Objekt ermöglichte. Es sah so aus:

aThing one: 'one';
  two: 'two';
  other.

Die Kaskadierung entwickelte sich in der Folge zu einer „ Methodenverkettung “, bei der „ Modifikatormethoden das Hostobjekt zurückgeben, so dass mehrere Modifikatoren in einem einzigen Ausdruck aufgerufen werden können. “ Die Methodenverkettung wurde später zu dem fließenden Schnittstellenkonzept , das wir heute häufig kennen und verwenden . Was Sie vorhaben, sieht sehr ähnlich aus.

Ayende Rahien erörtert, wie sich "fließende Benutzeroberfläche" deutlich genug von "Methodenverkettung" unterscheiden kann, um seinen eigenen Namen zu verdienen .

Fließende Schnittstellen sind häufig in einigen der neuen Tools zu finden, die in der verhaltensgesteuerten Entwicklung (BDD) verwendet werden, und haben sogar in NUnit , einem wichtigen .NET-Unit-Testing-Tool, in seinem neuen Constraint-Based-Assert-Modell Einzug gehalten .

Diese grundlegenden Ansätze wurden anschließend in anderen Sprachen implementiert, einschließlich Ruby, Python, C #, Objective-C und Java . Um etwas Ähnliches zu implementieren, sollten Sie sich mit der Idee des " Abschlusses " befassen , die für Verkettung und Sprachgewandtheit von grundlegender Bedeutung ist.

Vielleicht können Sie diese Modelle verbessern; So bekommen wir großartige neue Sprachen. Dennoch glaube ich, dass ein umfassendes Verständnis der Methodenverkettung und der fließenden Schnittstellen Ihnen einen guten Ausgangspunkt für die Weiterentwicklung Ihrer Ideen bietet!

John Tobler
quelle
2
+1 für die meisten bisher recherchierten Antworten, obwohl das Hinzufügen von Beispielen und die Neuformulierung zur besseren Lesbarkeit hilfreich sein könnten.
Haylem
@haylem, danke, dass du mich dazu gebracht hast, Beispiele und ein bisschen mehr Details hinzuzufügen!
John Tobler
3
Hochgestuft, weil es eine gute Beschreibung des fließenden Stils ist, aber beachten Sie bitte, dass dies wirklich nicht das ist, wonach das OP fragt. Wie ich es gelesen habe, bezieht sich die Frage auf das Durchsetzen von Parametern mit Teilen des Namens einer einzelnen Funktion oder Methode . Beachten Sie die vorgeschlagene Funktionsdeklaration: function GetTop(x)CustomersOfTheYear(y). Dies ist eine einzelne Funktion, nicht verkettete Aufrufe an zwei verschiedene Funktionen.
Caleb
@Caleb Ich denke, hier möchte er darauf hinweisen, dass diese Infix-Parameter ein unflexibler Weg sind, um entweder eine flüssige Schnittstelle (diese Antwort) oder benannte Parameter (eine andere Antwort) zu erstellen. Bisher habe ich keinen Vorteil für sie gesehen, dass eine fließende Benutzeroberfläche / benannte Parameter nicht besser abschneiden können.
Izkata
16

Objective-C macht das. Hier ist ein typischer Prototyp:

- (void) areaWithHeight: (float) height andWidth: (float) width;

So rufen Sie eine solche Methode auf:

float area = [self areaWithHeight: 75 andWidth: 20];

Objective-C wird hauptsächlich mit Cocoa für Mac OS X und Cocoa Touch für iOS verwendet, aber gcc erstellt Objective-C-Code auf nahezu jeder Plattform, auf der gcc funktioniert.

Mike Crawford
quelle
Tut mir leid, dass die Kugel ein Bindestrich sein sollte. Ich denke, Bindestriche im Antworttext werden als Aufschlag genommen. Bindestriche in Objective-C werden verwendet, um Methoden zu deklarieren, die zu einem Objekt gehören - [foo bar: x] wobei foo ein Objekt oder eine Klasseninstanz ist - während für Klassenmethoden Pluszeichen verwendet werden, was C ++ als statische Elementfunktionen bezeichnet.
Mike Crawford
13

In Common lisp können Sie Schlüsselwortargumente für eine Funktion wie die folgende definieren:

(defun area (&key width height)
    (* width height))

Die Funktion heißt so:

(area :width 2 :height 3)

In Ada brauchen Sie keine spezielle Deklaration - Sie können jede Prozedur oder Funktion alternativ aufrufen, indem Sie die Argumente der Reihe nach auflisten oder die Argumente wie folgt benennen:

a := area(width => 2, height => 3);

Schließlich enthält die Boost-Bibliothek eine Reihe von Hacks, um die Funktion zu C ++ hinzuzufügen: http://www.boost.org/doc/libs/release/libs/parameter/doc/html/index.html

han
quelle
5

Ich konnte den Namen nicht finden, aber es gibt ein Entwurfsmuster, um etwas Ähnliches zu erreichen, bei dem ein Funktionsaufruf ein neues Objekt zurückgibt, das wie beschrieben geändert wurde. Beispielsweise:

query = db.getTopCustomers(50).forYear(2010);

Es wird nicht sehr oft verwendet, da Ihre Daten sehr orthogonal sein müssen, um eine unüberschaubare Komplexität unter der Haube zu vermeiden, kann aber unter den richtigen Umständen nützlich sein.

Karl Bielefeldt
quelle
@Jesper, was ich sehe, ist jQuery-Verkettung sehr ähnlich. Habe ich recht?
Saeed Neamati
Ja, @Saeed, jQuery verwendet diesen Stil.
Karl Bielefeldt
5

Python hat Schlüsselwortparameter. Funktionsdefinitionsbeispiel

def getTopCustomers(count,year):
...

Funktionsaufruf Beispiel

x = getTopCustomers(year=1990, count=50)

(Ich verstehe, dass dies nicht ganz im Sinne der ursprünglichen Frage ist, aber wenn sich Schlüsselwortparameter in lisp qualifizieren, tun Sie dies auch. In Smalltalk und Objective-C sind die Schlüsselwörter zwischen den Argumenten jedoch wirklich Teil des Funktionsnamens / der Funktionssuche .)

David Andersson
quelle
Mir persönlich gefällt das besser, da Sie hier die Möglichkeit haben, die Funktion ohne die Parameternamen aufzurufen, wenn Sie die Reihenfolge der Parameter kennen
Pablo Mescher
4

Agda hat Mixfix-Notation

if_then_else x y z = case x of
                     True -> y
                     False -> z

 if cond then x else y

Wenn die Funktion mit einem Unterstrich benannt wird, kann sie mit einem dazwischen liegenden Argument in zwei Teile geteilt werden

Daniel Gratzer
quelle
4

Während keine Programmiersprache per se , Gurke nimmt Parameter in der Mitte der Funktionsnamen, die Leerzeichen enthalten können und wie Englisch aussehen soll.

Die 'Funktionen' sind jedoch in Ruby definiert

# definition
Given /^I calculate (.*) times (.*)$/ do |x, y|
    @result = x.to_i * y.to_i
end

Then /^the result should be (.*)$/ do |v|
    @result.should == v.to_i
end

# usage
Scenario:
    Given I calculate 6 times 9
    Then the result should be 42
Konfigurator
quelle
4

In TeX können Sie Makros mit einem „Argumentmuster“ definieren, was im Grunde bedeutet, dass Ihr Aufrufprotokoll frei ist. Für Ihr spezielles Beispiel könnten Sie verwenden

\def\getTopCustomers#1OfTheYear#2;{%
  The #1 top customers of the year #2.
}
\getTopCustomers 50 OfTheYear 2010;

Beachten Sie, dass es auch möglich ist, das loszuwerden, ;wenn Sie damit einverstanden sind, mit Registern zu spielen:

\newcount\customers
\newcount\year
\def\getTopCustomers#1OfTheYear{%
  \customers#1\relax
  \afterassignment\reallyGetTopCustomersOfTheYear
  \year
}
\def\reallyGetTopCustomersOfTheYear{%
  The {\the\customers} top customers of the year {\the\year}.
}
\getTopCustomers 50 OfTheYear 2010

Mit TeX können Sie den Lexer neu konfigurieren, und dank des oben verwendeten \afterassignmentMakros können Sie integrierte Prozeduren verwenden, um Zahlen zu lexen. Es ist sehr nützlich, sehr knappe Aufrufprotokolle zu definieren . Zum Beispiel ist es sehr plausibel, TeX-Makros zu schreiben, die die Markdown-Notation für Tabellen verstehen.

Nun fragen Sie: "Wie greife ich auf meine Datenbank zu, in der Kunden von TeX gespeichert werden?", Aber dies ist eine andere Frage. :)

In Common lisp ist es definitiv möglich, ein queryMakro zu definieren , mit dem Sie schreiben können

(query getTopCustomers 50 OfTheYear 2010)

Dabei werden getCustomers und OfTheYear als Symbole interpretiert. Es ist dann die Aufgabe des Makros, einen Sinn daraus zu machen. Common lisp eignet sich hervorragend für die Lesbarkeit von Code (ja, ich meine es ernst!), Da das Makrosystem die einfache Erstellung von Pseudosprachen ermöglicht, die auf Ihre Anwendung abgestimmt sind. (Ich denke, sie heißen Anwendungssprachen.)

PS: Es scheint, dass niemand C ++ zitiert. Die nächste, die Sie (ohne den Präprozessor) erhalten können, ist

query.getTopCustomers(50).OfTheYear(2010):

Der Trick besteht darin, getTopCustomerseinen Verweis auf query(oder irgendetwas anderes) zurückgeben zu lassen, der auch implementiert wird OfTheYear. Sie können dieses Beispiel auf eine kleine Abfragesprache aufbauen, müssen dann aber die endgültigen Eigenschaften identifizieren (einen Wert zurückgeben) oder eine finaliseMethode hinzufügen (die Suche durchführen und den Wert zurückgeben). Wenn Sie Lust dazu haben, können Sie auch Stream-Controller der STL imitieren und Dinge wie schreiben

query << getTopCustomers(50) << OfTheYear(2010) << flush;

dies geht aber wieder in richtung anwendungssprachen.

Edit: Ich habe @han Antworten übersehen, die auch Common Lisp und C ++ (aber nicht TeX!) Zitieren.

Michael Grünewald
quelle
2

In JavaScript oder einer anderen Sprache, die Closures unterstützt, können Sie eine Funktion wie diese curry :

function getTopCustomersFunc(count) {
    return function(year) {
       // run some query, return count customers from year
    }
}
var top20Func = getTopCustomersFunc(20);
var top20Customers2005 = top20Func(2005);
var top20Customers2008 = top20Func(2008);
jiggy
quelle
1
+1, weil dies interessant ist, aber denken Sie daran, dass die '20' in top20Func kein Parameter mehr ist, sondern Teil des Namens, der Sie an den Parameter erinnert. Sie könnte genauso gut sagen: var top5Func = getTopCustomersFunc(37);. Die '5' ist hier irreführend, aber der Code funktioniert genauso, als ob die Variable benannt worden wäre top37Func.
Caleb
1

Dies hängt von Ihrer Definition von "Sprache" ab. Mit dem Testframework von robotframework können Sie jedoch Schlüsselwörter auf diese Weise definieren. Aus ihrer Dokumentation zu eingebetteten Argumenten :

Select ${animal} from list | Open page | pet selection
                            | Select item from list | animal_list | ${amimal}

Das obige deklariert ein neues Schlüsselwort (im Wesentlichen eine Funktion) mit dem Namen "select $ {animal} from list", wobei '$ {animal}' ein Parameter ist. Sie nennen es "Katze aus Liste auswählen"

Bryan Oakley
quelle
Dies scheint mir wie SQL-Anweisungen. +1
Saeed Neamati
1

7 informieren.

To create (employee - a person) being paid (salary - a number):
    say "Welcome to your new job, [name of employee]!";
    choose a blank row from the table of employees;
    now the paid entry is the salary;
    now the name entry is the name of the employee.

Und so weiter.

Ron Newcomb
quelle
3
Dies erklärt nicht genau, wie die Syntax der Sprache funktioniert. Viele Menschen sind mit der Inform-Sprache nicht vertraut und können die Parameter nicht vom Aufruf oder seiner Verwendung in anderen Bereichen des Codes unterscheiden.
Diese Sprache fasziniert mich. Die Antwort war mit nur einem nicht ausgearbeiteten Codebeispiel ziemlich kurz, ebenso wie die vom OP gegebenen Beispiele (wenn auch mit konventionelleren Sprachen). Wiki hat einen schönen Artikel darüber zu lesen.
YoYo
0

Die Schlüsselwörter von Common Lisp wurden bereits erwähnt, aber die Makros von Common Lisp ermöglichen dies auch ziemlich einfach:

(defmacro get-my-element (from list at index)
  `(nth ,index ,list))

Das kann man dann so nennen:

(get-my-element from '(a b c) at 1) ;returns B

Eine Sache, die zu beachten ist, ist, dass die Bedingungen fromund atnicht durchgesetzt werden. Es muss etwas da sein, sie komplett zu überspringen ist ein Fehler, aber da das Makro sie einfach verwirft, ist es unwichtig, was sie nicht lesbar sind (was wichtig ist):

(get-my-element from '(a b c) 3 1) ;also returns B
8bittree
quelle
-2

In Clojure werden häufig Schlüsselwortargumente für Situationen wie diese verwendet, z.

(get-customers :top 50 :year 2010)

Schlüsselwortargumente sind ziemlich flexibel, sie können optional sein und Standardeinstellungen usw. haben.

mikera
quelle
-4

Sie können dies in Python emulieren. Beispielsweise,

def GetTop(customers, ofYear):
  #write some code here to get the customers

print GetTop(customers=50 ofYear=2010)

oder auch

def GetTop(**args):
  #write some code here using the same parameter names as above

print GetTop({customers: 50, ofYear: 2010})
coder108
quelle
1
Die Syntax in Ihrem zweiten Beispiel ist falsch. Außerdem hat dies bereits jemand gepostet.
Winston Ewert
Dies wiederholt sich lediglich Punkt gemacht (und viel besser erklärt) in diesem Stand der Antwort , die 3 Jahre früher geschrieben wurde
gnat