Bei einer Diskussion über statische und Instanzmethoden denke ich immer, dass Sqrt()
dies eine Instanzmethode mit Zahlentypen anstelle einer statischen Methode sein sollte. Warum das? Es funktioniert offensichtlich auf einem Wert.
// looks wrong to me
var y = Math.Sqrt(x);
// looks better to me
var y = x.Sqrt();
Werttypen können natürlich Instanzmethoden haben, da es in vielen Sprachen eine Instanzmethode gibt ToString()
.
Um einige Fragen aus den Kommentaren zu beantworten: Warum sollte 1.Sqrt()
nicht legal sein? 1.ToString()
ist.
In einigen Sprachen sind Methoden für Werttypen nicht zulässig, in einigen Sprachen jedoch. Ich spreche über diese, einschließlich Java, ECMAScript, C # und Python (mit __str__(self)
definierten). Gleiches gilt für andere Funktionen wie ceil()
, floor()
etc.
1.sqrt()
gültig?Sqrt(x)
sieht viel natürlicher aus alsx.Sqrt()
Wenn das bedeutet, dass der Klasse in einigen Sprachen die Funktion vorangestellt wird, bin ich damit einverstanden. Wenn es gab eine Instanzmethode dannx.GetSqrt()
wäre besser geeignet sein , um anzuzeigen , dass es Rückkehr Wert anstatt Modifizieren der Instanz.Antworten:
Es ist völlig eine Wahl der Sprachgestaltung. Dies hängt auch von der zugrunde liegenden Implementierung primitiver Typen und den damit verbundenen Leistungsaspekten ab.
.NET hat nur eine statische
Math.Sqrt
Methode, die auf a einwirktdouble
und a zurückgibtdouble
. Alles andere, was du ihm übergibst, muss besetzt oder zu a befördert werdendouble
.Auf der anderen Seite haben Sie Rust, das diese Operationen als Funktionen für die folgenden Typen verfügbar macht :
Rust verfügt jedoch auch über eine universelle Funktionsaufrufsyntax (wie bereits erwähnt), sodass Sie auswählen können, was Sie möchten.
quelle
x.Sqrt()
, können Sie dies tun.public static class DoubleExtensions { public static double Sqrt( this double self) { return Math.Sqrt(self); } }
Sqrt(x)
msdn.microsoft.com/en-us/library/sf0df423.aspx sein .Angenommen, wir entwerfen eine neue Sprache und möchten
Sqrt
eine Instanzmethode sein. Also schauen wir uns diedouble
Klasse an und beginnen mit dem Entwerfen. Es hat offensichtlich keine Eingaben (außer der Instanz) und gibt a zurückdouble
. Wir schreiben und testen den Code. Perfektion.Aber die Quadratwurzel einer ganzen Zahl zu ziehen ist auch gültig, und wir wollen nicht jeden zwingen, in ein Double zu konvertieren, nur um eine Quadratwurzel zu ziehen. Also ziehen wir um
int
und beginnen mit dem Entwerfen. Was kommt zurück? Wir könnten ein zurückgebenint
und es nur für perfekte Quadrate arbeiten lassen oder das Ergebnis auf das nächste rundenint
(ohne die Debatte über die richtige Rundungsmethode im Moment). Aber was ist, wenn jemand ein nicht ganzzahliges Ergebnis haben möchte? Sollten wir zwei Methoden haben - eine, die eine zurückgibt,int
und eine, die eine zurückgibtdouble
(was in einigen Sprachen nicht möglich ist, ohne den Namen zu ändern)? Also entscheiden wir, dass es a zurückgeben solldouble
. Jetzt setzen wir um. Die Implementierung ist jedoch identisch mit der, für die wir verwendet habendouble
. Kopieren und Einfügen? Umwandeln wir die Instanz in einedouble
und rufen diese Instanzmethode auf? Stellen Sie die Logik in eine Bibliotheksmethode, auf die von beiden Klassen aus zugegriffen werden kann. Wir rufen die BibliothekMath
und die Funktion aufMath.Sqrt
.Wir haben noch nicht einmal andere Argumente angesprochen:
GetSqrt
da es einen neuen Wert zurückgibt , anstatt die Instanz zu ändern?Square
?Abs
?Trunc
?Log10
?Ln
?Power
?Factorial
?Sin
?Cos
?ArcTan
?quelle
1.sqrt()
vs1.1.sqrt()
(Ghads, das sieht hässlich aus), haben sie eine gemeinsame Basisklasse? Was ist der Vertrag für seinesqrt()
Methode?1.1.Sqrt
dargestellt wurde. Klug.Mathematische Operationen sind oft sehr leistungsabhängig. Aus diesem Grund möchten wir statische Methoden verwenden, die zum Zeitpunkt der Kompilierung vollständig aufgelöst (und optimiert oder inline) werden können. Einige Sprachen bieten keinen Mechanismus zur Angabe statisch versendeter Methoden. Darüber hinaus hat das Objektmodell vieler Sprachen einen erheblichen Speicheraufwand, der für "primitive" Typen wie z
double
.In einigen Sprachen können Funktionen definiert werden, die Methodenaufrufsyntax verwenden, aber tatsächlich statisch gesendet werden. Ein Beispiel sind Erweiterungsmethoden in C # 3.0 oder höher. Nicht-virtuelle Methoden (z. B. die Standardeinstellung für Methoden in C ++) sind ein weiterer Fall, obwohl C ++ keine Methoden für primitive Typen unterstützt. Selbstverständlich können Sie in C ++ eine eigene Wrapper-Klasse erstellen, die einen primitiven Typ mit verschiedenen Methoden verziert, ohne dass dies zu einem Laufzeitaufwand führt. Sie müssen die Werte jedoch manuell in diesen Wrapper-Typ konvertieren.
Es gibt einige Sprachen, die Methoden für ihre numerischen Typen definieren. Dies sind normalerweise hochdynamische Sprachen, in denen alles ein Objekt ist. Hier spielt die Leistung eine untergeordnete Rolle für die konzeptionelle Eleganz, aber diese Sprachen werden im Allgemeinen nicht für die Zahlenverarbeitung verwendet. Diese Sprachen verfügen jedoch möglicherweise über ein Optimierungsprogramm, mit dem Vorgänge auf Grundelementen „entpackt“ werden können.
Mit den technischen Überlegungen können wir überlegen, ob eine solche methodenbasierte mathematische Schnittstelle eine gute Schnittstelle wäre. Es treten zwei Probleme auf:
42.sqrt
erscheint vielen Benutzern viel fremder alssqrt(42)
. Als mathematikintensiver Benutzer würde ich es vorziehen, meine eigenen Operatoren anstelle der Punktmethodenaufrufsyntax zu erstellen.mean
,median
,variance
,std
,normalize
auf numerischen Listen oder die Gamma - Funktion für Zahlen) kann nützlich sein. Für eine Allzwecksprache belastet dies lediglich die Benutzeroberfläche. Durch das Verknüpfen nicht notwendiger Vorgänge in einen separaten Namespace wird der Zugriff auf den Typ für die Mehrheit der Benutzer erleichtert.quelle
transpose
undmean
dass gibt es nur eine einheitliche Schnittstelle mit NumPy Arrays zu schaffen, das die realen Arbeitspferd Datenstruktur ist.Ich würde durch die Tatsache motiviert sein, dass es eine Menge spezieller mathematischer Funktionen gibt, und anstatt jeden mathematischen Typ mit allen (oder einer zufälligen Teilmenge) dieser Funktionen zu füllen, ordnen Sie sie einer Hilfsklasse zu. Andernfalls würden Sie entweder Ihren Tooltip zur automatischen Vervollständigung verschmutzen oder die Leute zwingen, immer an zwei Stellen nachzuschauen. (Ist
sin
wichtig genug, um Mitglied zu seinDouble
, oder ist es in derMath
Klasse zusammen mit Inzuchten wiehtan
undexp1p
?)Ein weiterer praktischer Grund ist, dass es möglicherweise verschiedene Möglichkeiten gibt, numerische Methoden mit unterschiedlichen Kompromissen in Bezug auf Leistung und Präzision zu implementieren. Java hat
Math
und es hat auchStrictMath
.quelle
Math.<^space>
? Dieser Auto-Vervollständigungs-Tooltip wird ebenfalls verschmutzt. Umgekehrt denke ich, dass Ihr zweiter Absatz wahrscheinlich eine der besseren Antworten hier ist.Sie haben richtig bemerkt, dass hier eine merkwürdige Symmetrie vorliegt.
Ob ich sage
sqrt(n)
odern.sqrt()
nicht, beide drücken dasselbe aus, und welches Sie bevorzugen, ist mehr eine Frage des persönlichen Geschmacks als alles andere.Das ist auch der Grund, warum bestimmte Sprachdesigner ein starkes Argument dafür haben, die beiden Syntaxen austauschbar zu machen. Die Programmiersprache D ermöglicht dies bereits mit einer Funktion namens Uniform Function Call Syntax . Eine ähnliche Funktion wurde auch für die Standardisierung in C ++ vorgeschlagen . Wie Mark Amery in den Kommentaren betont, erlaubt Python dies auch.
Das ist nicht ohne Probleme. Die Einführung einer solchen grundlegenden Syntaxänderung hat weitreichende Konsequenzen für den vorhandenen Code und ist natürlich auch ein Thema kontroverser Diskussionen unter Entwicklern, die seit Jahrzehnten darauf trainiert sind, die beiden Syntaxen als Beschreibung verschiedener Dinge zu betrachten.
Ich denke, nur die Zeit wird zeigen, ob die Vereinigung der beiden auf lange Sicht machbar ist, aber es ist definitiv eine interessante Überlegung.
quelle
self
als ersten Parameter und wenn Sie die Methode als Eigenschaft einer Instanz anstatt als Eigenschaft der Klasse aufrufen, wird die Instanz implizit als erstes Argument übergeben. Daher kann ich schreiben"foo".startswith("f")
oderstr.startswith("foo", "f")
und ich kann schreibenmy_list.append(x)
oderlist.append(my_list, x)
.Neben der Antwort von D Stanley muss man sich auch mit Polymorphismus auseinandersetzen. Methoden wie Math.Sqrt sollten immer denselben Wert an dieselbe Eingabe zurückgeben. Das Festlegen einer statischen Methode ist eine gute Möglichkeit, um diesen Punkt zu verdeutlichen, da statische Methoden nicht überschrieben werden können.
Sie haben die ToString () - Methode erwähnt. Hier möchten Sie möglicherweise diese Methode überschreiben, damit die (Unter-) Klasse auf andere Weise als String als übergeordnete Klasse dargestellt wird. Sie machen es also zu einer Instanzmethode.
quelle
Nun, in Java gibt es einen Wrapper für jeden Basistyp.
Und Basistypen sind keine Klassentypen und haben keine Elementfunktionen.
Sie haben also die folgenden Möglichkeiten:
Math
.Schließen wir Option 4 aus, weil ... Java Java ist und Anhänger es so mögen.
Jetzt können wir ausschließen auch Option 3 , da während Objekte Zuteilung ziemlich billig ist, ist es nicht frei ist, und tun , dass immer und immer wieder nicht aufaddieren.
Zwei Fehler, einer noch zu töten: Option 2 ist ebenfalls eine schlechte Idee, da dies bedeutet, dass jede Funktion für jeden Typ implementiert werden muss , man sich nicht darauf verlassen kann, die Konvertierung zu erweitern, um die Lücken zu füllen , da sonst die Inkonsistenzen wirklich schaden.
Und wenn man sich das ansieht
java.lang.Math
, gibt es viele Lücken, insbesondere für Typen, die kleiner als dieint
jeweiligen sinddouble
.Am Ende ist der klare Sieger also die erste Option, die alle an einem Ort in einer Utility-Funktionsklasse sammelt.
Zurück zu Option 4, etwas in dieser Richtung passierte tatsächlich viel später: Sie können den Compiler auffordern, alle statischen Member einer beliebigen Klasse zu berücksichtigen, wenn Sie Namen seit geraumer Zeit auflösen.
import static someclass.*;
Abgesehen davon haben andere Sprachen dieses Problem nicht, entweder weil sie keine Vorurteile gegen freie Funktionen (optional unter Verwendung von Namespaces) haben oder weit weniger kleine Typen.
quelle
Math.min()
in allen Wrapper-Typen.Math.sqrt()
wurde zur gleichen Zeit wie der Rest von Java erstellt. Als also die Entscheidung getroffen wurde, sqrt () einzufügen,Math
gab es keine historische Trägheit für Java-Benutzer, die es "so mögen". Während es mit nicht viel von einem Problem gibtsqrt()
, ist das Überlastungsverhalten vonMath.round()
schrecklich. Die Möglichkeit , Mitglied Syntax mit Werten des Typs zu verwendenfloat
unddouble
würde dieses Problem vermieden werden kann.Ein Punkt, den ich nicht explizit erwähne (obwohl amon darauf anspielt), ist, dass die Quadratwurzel als "abgeleitete" Operation gedacht werden kann: Wenn die Implementierung dies nicht für uns bereitstellt, können wir unsere eigene schreiben.
Da die Frage mit Sprachdesign markiert ist, können wir eine sprachunabhängige Beschreibung in Betracht ziehen. Obwohl viele Sprachen unterschiedliche Philosophien haben, ist es in allen Paradigmen sehr verbreitet, die Kapselung zu verwenden, um Invarianten zu bewahren. dh zu vermeiden, einen Wert zu haben, der sich nicht so verhält, wie es der Typ vermuten lässt.
Wenn wir zum Beispiel eine Implementierung von Ganzzahlen mit Maschinenwörtern haben, möchten wir wahrscheinlich die Darstellung irgendwie kapseln (z. B. um zu verhindern, dass Bitverschiebungen das Vorzeichen ändern), aber gleichzeitig benötigen wir immer noch Zugriff auf diese Bits, um Operationen wie zu implementieren Zusatz.
Einige Sprachen können dies mit Klassen und privaten Methoden implementieren:
Einige mit Modulsystemen:
Einige mit lexikalischem Geltungsbereich:
Und so weiter. Doch keines dieser Mechanismen sind für die Umsetzung Quadratwurzel benötigt: sie umgesetzt werden kann , die mit der öffentlichen Schnittstelle eines numerischen Typ, und daher macht es keinen Zugriff auf die verkapselte Implementierungsdetails müssen.
Daher hängt die Position der Quadratwurzel von der Philosophie / dem Geschmack der Sprache und des Bibliotheksdesigners ab. Einige können es als "innerhalb" der numerischen Werte festlegen (z. B. als Instanzmethode festlegen), andere können es auf der gleichen Ebene wie die primitiven Operationen festlegen (dies kann eine Instanzmethode bedeuten, oder es kann bedeuten, dass Sie außerhalb der numerische Werte, aber innerhalb desselben Moduls / derselben Klasse / desselben Namespaces (z. B. als eigenständige Funktion oder statische Methode), können einige diese in eine Sammlung von "Hilfsfunktionen" einfügen, andere können sie an Bibliotheken von Drittanbietern delegieren.
quelle
In Java und C # ist ToString eine Objektmethode, der Stamm der Klassenhierarchie, sodass jedes Objekt die ToString-Methode implementiert. Für einen Integertyp ist es nur natürlich, dass die Implementierung von ToString auf diese Weise funktioniert.
Sie argumentieren also falsch. Der Grund, warum Werttypen ToString implementieren, ist nicht, dass manche Leute es so fanden: Hey, lassen Sie uns eine ToString-Methode für Werttypen haben. Das liegt daran, dass ToString bereits vorhanden ist und dass die Ausgabe "am natürlichsten" ist.
quelle
object
eineToString()
Methode zu haben . Das ist in Ihren Worten "einige Leute waren wie: Hey, lassen Sie uns eine ToString-Methode für Werttypen haben".Im Gegensatz zu String.substring ist Number.sqrt nicht wirklich ein Attribut der Zahl, sondern ein neues Ergebnis, das auf Ihrer Zahl basiert. Ich denke, dass die Übergabe Ihrer Nummer an eine Quadrierungsfunktion intuitiver ist.
Darüber hinaus enthält das Math-Objekt andere statische Elemente, und es ist sinnvoller, sie zusammenzufassen und einheitlich zu verwenden.
quelle