Diskussion:Signatur (Programmierung)

aus Wikipedia, der freien Enzyklopädie
Letzter Kommentar: vor 8 Jahren von GiftBot in Abschnitt Defekte Weblinks
Zur Navigation springen Zur Suche springen

Rückgabetyp gehört nicht dazu

[Quelltext bearbeiten]

...die genaue Begründung kriege ich im Moment nicht zusammen. In C++ wird aber beispielsweise überprüft, ob der Rückgabetyp einer überschriebenen Methode zur Festlegung in der Basisklasse passt. Möglich ist

class A { A* clone() const; };
class B: public A { B* clone() const; };

Allerdings gehört bei C++ auch das const hinter der Methodendeklaration dazu (ein für C++-Programmierer manchmal schmerzhafter Aspekt). --Peu 23:46, 24. Jun 2006 (CEST)

Nehmen wir an, der Rückgabewert gehörte dazu, wo läge der Unterschied zum Prototyp? Darky77 02:19, 28. Jun 2006 (CEST)

Auch in Java muss der Rückgabetyp nicht genau übereinstimmen, um die Methode zu überschreiben, wie folgendes Beispiel zeigt:

public class Vererbungstest {

	static class A {}
	static class B extends A {}
	static class C {}
	
	static class L {
		A f() { System.out.println("L.f"); return null; };
		A g() { System.out.println("L.g"); return null; };
		A h() { System.out.println("L.h"); return null; };
		
		void i(A a) { System.out.println("L.i"); };
		void j(A a) { System.out.println("L.j"); };
		void k(A a) { System.out.println("L.k"); };
	}
	
	static class M extends L {
		@Override
		A f() { System.out.println("M.f"); return null; };
		@Override
		B g() { System.out.println("M.g"); return null; };
		// geht gar nicht
//		C h() { System.out.println("M.h"); return null; };

		@Override
		void i(A a) { System.out.println("M.i"); };
		// kein @Override
		void j(B a) { System.out.println("M.j"); };
		// kein @Override
		void k(C a) { System.out.println("M.k"); };
	}
	
	public static void main(String[] args) {
		L x = new M();		
		x.f(); x.g();		
		x.i(null); x.j(null); x.k(null);
	}

}

Beim Überschreiben ist in Java auch erlaubt, dass der Rückgabetyp eine Erweiterung des ursprünglichen Rückgabetypen ist. Bei Parametern ist das hingegen nicht der Fall. Andererseits ist auch kein beliebiger Rückgabetyp beim Überschreiben erlaubt. Somit kommt dem Rückgabetypen bezüglich des Überschreibens eine Sonderstellung in der Signatur zu. (nicht signierter Beitrag von 137.248.121.146 (Diskussion) )

Man nennt das übrigens auch en:Covariant return type. Wenn ich irgendwann mal Zeit finde, schreibe ich was dazu. --jpp ?! 16:46, 12. Dez. 2006 (CET) PS: Wie kommst du auf die Idee, dass java.util.Date deprecated sei?Beantworten


Also ich glaube auch, dass die Signatur nur aus den Parametern besteht. Auf der Seite von Dumke steht z.B.: "Die Menge der Operatoren eines Typs T zusammen mit ihrer Stelligkeit heißt Signatur von T." Im Skript meines Profs steht ebenfalls: "Die Signatur einer Funktion ist die Anzahl, der Typ und die Reihenfolge der Argumente."

Also, ihr behandelt hier nur Java und weitere syntaktisch von C abgeleitete Programmiersprachen. Mir ist aber nicht bekannt und ich habe nach kurzer Recherche auch keinen Hinweis gefunden, dass dies allgemein für alle Programmiersprachen gilt und ich kann mir durchaus vorstellen, dass für manche Sprachen durchaus die Rückgabetypen (ja, es gibt Sprachen mit mehreren Rückgabewerten) und die Parameternamen für die Signatur relevant sind. Solange wir keinen Negativbeleg haben, ist die jetzige Aussage eine unzulässige Verallgemeinerung. Ich werde sie gleich anpassen. --Leo141 12:58, 6. Aug. 2010 (CEST)Beantworten
hier der Negativbeleg:Funktion (Mathematik). Alle Funktionen sind demnach (u.a) rechtseindeutige/funktionale Relationen, d.h. fuer eine eindeutige Eingabe (hier: die Parameterwerte der Funktion) kann es max. nur 1 entsprechenden Rueckgabewert geben, der muss also immer gleich sein. Der logische Umkehrschluss: Werden bei gleichen Eingabeparameterwerten jeweils unterschiedliche Ergebnisse geliefert, muessen es unterschiedliche Funktionen gewesen sein.--178.24.108.17 20:23, 12. Aug. 2010 (CEST)Beantworten
Nein, Funktionen und Prozeduren in der Informatik entsprechen nur in Spezialfällen (Funktionale Programmierung) mathematischen Funktionen. Deine Argumentationskette enthält noch weitere Fehler – ist aber egal, bei Wikipedia zählen im Zweifel nur externe Belege (WP:Q). Ansonsten siehe den Beitrag von Daniel5Ko [1] --92.79.180.75 13:13, 31. Aug. 2010 (CEST)Beantworten
Das ist doch nich dein Ernst, dass du hier fuer die Funktionsdefinition die Quelle verlangst -oder hast du kein Mathebuch und suchst in deiner Bibliothek in der Kochbuchecke? dann schau doch mal hier: Funktion (Mathematik)#Literatur ;-) Sorry, aber mal im Ernst, wo finde ich denn eine Quelle zum Artikel Funktion (Programmierung), speziell die Definition? Zu dem Haskellbeispiel behaupte ich einfach mal, dass die Angabe des Rueckgabetyps hier bei jedem Aufruf angegeben werden muss und damit zum Funktionsnamen gehoert, also nix mit gleicher Funktionsaufruf aber unterschiedliche Rueckgabetypen. mfG -- Madhatter 19:58, 24. Nov. 2010 (CET)Beantworten
Wozu dient denn die Signatur? Doch wohl zum Auflösen von statischen Überladungen. Man kann genauso gut argumentieren, die Typen der Parameter gehörten zum Funktionsnamen. Siehe auch Name-Mangling bei C++-Compilern, die an an C orientierte Linker denken müssen (oder wollen). C++ kennt Funktionsüberladung, C nicht.
Man muss die Typen der Argumente beim Aufruf nicht angeben, weil der Compiler in C++ aus den übergebenen Ausdrücken diese zur Compilezeit ermitteln kann, außer z.B. beim null-Literal. Genauso muss man den Rückgabetyp in Haskell auch nicht angeben, wenn der ermittelt werden kann. --Daniel5Ko 21:20, 24. Nov. 2010 (CET)Beantworten
Das Problem ist ja nicht ob die Parametertypen nun zum Funktionsnamen gehörten oder nicht -dass dem nicht so ist ist eine Definitionssache, also festgelegt. Dass in C kein Überladen vorgesehen ist, liegt an der Sprachdefinition, das könnte man als Mangel ansehen. C kennt dafür aber als Ersatz Funktionszeiger. Hier geht es aber um die Funktionsauflösung. Angenommen, es gäbe zwei Funktonen, die sich nur durch den Rückgabetyp unterscheiden würden (zum Funktionsnamen gehört ja eigentlich auch Namespace bzw. Paketname -der nur da ist das Programmieren und Verwalten komfortabler zu machen, und bei Methoden die Klasse), dann müsste dem Compiler schon vorher bekannt sein, welcher Rückgabetyp erwartet wird. Er müsste den passenden Aufruf durch "intellgent" aus der Semantik ermitteln und ausserdem die per Rueckgabetyp definierten Funktionen in die "mathematisch" definierte Form überführen. Wenn Haskell das (manchmal) kann, ist der Artikel so wie jetzt natürlich richtig (und ich habe mal wieder was dazugelernt :-). Eine Quellenfeferenz zum Haskellbeispiel wäre schön oder auch ein Verweis auf ein kompilierbares Quellcodebeispiel.
-- Madhatter 01:46, 25. Nov. 2010 (CET)Beantworten
Ich verstehe nicht, was du mit ' die per Rueckgabetyp definierten Funktionen in die "mathematisch" definierte Form überführen ' meinst. Mathematiker überladen ihre Funktions- und Konstantennamen auch. Siehe zum Beispiel Ringtheorie. Hier sind 0 und 1 Konstanten (a.k.a. nullstellige Funktionen), aber was sie wirklich genau sind, hängt eben "von ihrem Rückgabetyp" ab. Haskell verfolgt übrigens einen ähnlich strukturierten Ansatz: um Überladung zu ermöglichen, wurde gleich das ganz neue Konzept der Typklassen eingeführt. Statt lose herumliegende Funktionen mit "zufällig" gleichem Namen werden da gleich mehrere Namen zusammengefasst, und will man einen von ihnen neu definieren, muss man das mit den anderen auch tun. Hier ein paar Papers: http://homepages.inf.ed.ac.uk/wadler/topics/type-classes.html .
Das Beispiel mit read 42 ist in GHCi oder Hugs direkt ausprobierbar, denn read ist Bestandteil der Standardbibliothek. Ein "kompilierbares Quellcodebeispiel" ist also gewissermaßen vorhanden.
Zu ' Wenn Haskell das (manchmal) kann [...] ': eigentlich kann es das ziemlich oft, u.U. mit ein wenig Betrug durch defaulting. In dem Ausdruck read "42" + read "23" + length "hallo", wo + und read überladen sind, wird (ohne defaulting) ermittelt, dass der Gesamtausdruck den Typ Int hat, und jenes read mit dem Typ String -> Int gemeint sein muss.
Gruß, --Daniel5Ko 18:50, 25. Nov. 2010 (CET)Beantworten
Hallo Daniel, ich muss ehrlich gestehen dass ich mathematisch und informatisch nicht allzu versiert bin. Für mich ist eine Funktion eine Relation, die jedem Element aus einer definierten Eingangsmenge (Urbildmenge) ein -und nur ein Element aus einer definierten Zielmenge (Bildmenge) zuordnet. Die Begrenzung auf maxial ein Elemnt aus der Bildmenge macht diese Relation rechtseindeutig bzw. funktional. Wenn ich das jetzt auf die Funktionen aus den mir bekannten Programmersprachen uebertrage, dann bezeichnet der Funktionsname die Relation und die Typen der Eingabelemente stellen die Definitionsmenge dar (bei mehreren Elementen als Tupel), beides zusammen als Signatur. Dabei ist es jetzt moeglich, dass der Relationsname (Funktionsname) die Definitionsmenge implizit mit einschließt, also die Relation durch Funktionsname und Argumenttypen zusammen zugeordnet wird. Deshalb geht jetzt Ueberladung, einfach weil jetzt der selbe Funktionsname zusammen mit anderen Argumenttypen eine ganz andere Relation bezeichnen kann, eben eine andere Signatur. So weit wie hier dargelegt stimmt der mathematische Funktionsbegriff mit Funktionsdeklarationen in den mir bekannten Programmiersprachen überein.
Das von dir angeführte Beispiel habe ich so verstanden: Wenn jetzt so wie in deinem Beispiel die überladenen read-Funktionen im Quelltext mit Funktionsnamen und Argumenttypen übereinstimmen, müsste ein Regelwerk definiert sein, dass die passende Funktion je nach Kontext/Bedeutung/Semantik auswählt, weil die Funktionen sonst nicht unterscheidbar wären, sich als ein und die selbe Funktion darstellen und deshalb ohne Zusatzinformation das Gebot der Rechtseindeutigkeit verletzen. Jetzt könnte Z.B. die Regel definiert sein, dass der Rückgabewert ein Integer ist, wenn als Rückgabewert nur ein Integer-Typ verlangt wird (weil die ' +' Funktion schon ein Integer-Argument hat) und ein String, wenn eine Zeichenkette erwartet wird. Durch die Auswertung nach diesem Regelwerk kann jetzt die passende Funktion ausgewählt werden und die Rechtseindeutigkeit ist wieder gegeben. Das mit dem ' in die mathematische Form ueberfuehren ' war irgendwie doof von mir ausgedrückt. Ich meinte damit, dass der Interpreter die Rechtseindeutigkeit herstellt. Hoffentlich ist mir bei meinen Gedanken kein grober Schnitzer unterlaufen und das Ganze war für die Tonne... Wenn nicht, könnte der Gedankengang eventuell passend mit in den Artikel eingearbeitet werden.. Gruß, -- Madhatter 16:45, 27. Nov. 2010 (CET)Beantworten
Beim Aufruf einer überladenen Funktion in z.B. Java gibt man die Typen der Argumente nicht direkt an. Sie werden durch ein Regelwerk implizit vom Compiler ermittelt (ziemlich einfaches Regelwerk: "nimm einfach die statischen Typen der Ausdrücke, die beim Aufruf übergeben werden"). Es spricht nichts dagegen, dass man das u.U. auf Rückgabetypen verallgemeinern kann, mit einem etwas komplizierteren Regelwerk. ;) Und in beiden Fällen kann es zu Uneindeutigkeiten kommen: null in Java hat jeden Referenztyp als statischen Typ. (Ich weiß, ich wiederhole mich, aber der Punkt scheint nicht richtig 'rübergekommen zu sein: Naheliegende Erweiterung mit ähnlichen Eigenschaften.)
Zur Rechtseindeutigkeit: Nein, darum geht's überhaupt nicht. Mathematischen Funktionen ist es egal, welchen Namen man ihnen gibt. Sie behalten die selben Eigenschaften; und sie sind immer rechtseindeutig. Worum es hier geht: wie unterscheide bzw. referenziere ich verschiedene Funktionen, denen ich (zufällig oder absichtlich) den selben Namen gegeben habe? Die Eindeutigkeit der Funktionsauswahl stellt nicht die Funktion (wie sollte sie), und nicht der Interpreter (i.A. viel zu ineffizient), sondern der Compiler her (Funktionsüberladung in dynamischen Sprachen geht in Richtung Seltsamkeit). Gruß, --Daniel5Ko 19:44, 27. Nov. 2010 (CET)Beantworten
Ja, das ist natürlich klar, dass immer der Compiler/Interpreter die passenden Funktionen auswählt. Das Ueberladen in dynamischen Sprachen ist gar nicht so seltsam wie es scheint. Wenn man eine Klasse ableitet, wird ja nur die Superklasse erweitert: neue Methoden und Felder koennen dazukommen und auch Methoden ueberschrieben werden (Overriding). Objekte aus Abgeleiteten Klassen sind hier sozusagen "getunte" Objekte im vergleich zu Objekten die aus der Ober/Superklasse instanziert werden. Wenn du jetzt eine Objektinstanz mit dem Klassentyp der Oberklasse initialisierst, aber dazu den Konstruktor einer Unterklasse nimmst, so etwa: Oberklassetyp objekt1 = new Unterklassetyp(); kriegst du ein Objekt vom Typ Unterklasse, mit allen Methoden und Feldern. Alle Methoden verweisen auf die Unterklasse. Aber: Der Compiler behandelt das Objekt so als wäre es vom Typ Oberklasse, und das ist ganz einfach. Die Regel heisst hier: Vergiss alle Methoden u. Felder, die nicht auch in der Oberklasse deklariert sind.. Also kannst du die Methoden, die erst in der Unterklasse definiert wurden nicht aufrufen, und dass, obwohl sie da sind. Der Oberklassetyp wirkt hier wie eine Maske. Dennoch haben die Objektmethoden eine eindeutige Signatur -nämlich die von Unterklassetyp. Wenn du jetzt die Maske wegnimmst, kannst du wieder auf alle Methoden und Felder zugreifen: Unterklassetyp objekt2 = objekt1; //Javabeispiel
Jetzt hast du zwei Referenzen auf ein Objekt von Typ Unterklasse, die eine ist beschränkt, die andere nicht. Die Methodensignatur ist hier aber immer Unterklasse.Methodenname(ArgumentTyp.. ), also rechtseindeutig. Da ist keine Semantik noetig, der Trick ist nur die Verschleierung/Kastration durch die Maske. Also nicht den Maskentyp mit der echten Signatur verwechseln. Hättest du bei der Instanzierung auch den Konstruktor der Oberklasse genommen, hätte die entsprechende Methode die Signatur Oberklasse.Metodenname(ArgumentTyp..) -und object2 = objekt1; wäre ein Fehler, da ein OberklasseObjekt nunmal nicht alle Methoden bzw Felder seiner Unterklassen haben kann/könnte. mfG, -- Madhatter 23:00, 27. Nov. 2010 (CET)Beantworten
ah so, was ich noch sagen wollte: Natürlich spielen die Namen auch bei der mathematischen Funktionsdefinition eine Rolle. Wie wollte man denn sonst die Funktionen auseinanderhalten, wenn die zwar unterschiedliche Relationen bezeichnen aber gleich benannt sind (Name und Definitionsbereich gleich). -- Madhatter 23:08, 27. Nov. 2010 (CET)Beantworten
Sorry, hab mich hier mit dem Beispiel zu weit aus dem Fenster gelehnt, vergiss das mit "die Maske aufdecken", ist Bullschit, scheint nicht mehr zu gehen, habs noch mal ausprobiert. Einmal Kastriert, immer kastriert... Und mit gleichen Funktionsnamen in mathematischem Kontext meine ich: die selben Funktionsnamen in einer Formel verwendet. mfG -- Madhatter 23:55, 27. Nov. 2010 (CET)Beantworten
(Antwort ohne Berücksichtigung der Bullschit-Ergänzung — keine Lust, das jetzt anzupassen):
Hi. OO-mäßiges Single-Dispatch ist ein anderes Thema (also Methoden überschreiben etc.). Hier wird tatsächlich erst zur Laufzeit ausgewählt, welche Methode aufgerufen wird, aber nicht anhand einer Signatur (bei Sprachen, die beides machen, wie C++, Java, C#, ... hat der Compiler hier bereits eine Entscheidung getroffen), sondern anhand jener VMT, an die man über den jeweiligen this-Zeiger 'rankommt. In sofern ist deine Beschreibung (u.a. "Also kannst du die Methoden, die erst in der Unterklasse definiert wurden nicht aufrufen") irreführend: natürlich kann man überschriebene Methoden aufrufen. das kommt natürlich drauf an, was "definiert wurden" heißt.
In dynamischen Sprachen mit OO-Klassen (Python, Ruby, ...) hat man in der Regel nur diese Form, und man kann mMn keinesfalls von Signaturen sprechen, und signaturbasiertes Überladen geht einfach nicht — würde auch wenig Sinn ergeben.
In Sprachen wie C++, Java, C#, ... dient die Signatur auch dazu, zu bestimmen, welcher Eintrag in der VMT beim Overriden denn nun gemeint ist.
Zu den "Namen" von Funktionen: Nein, die gehören nicht dazu. Man braucht sie nicht. Man kann sogar Funktionen, für die man lediglich eine rekursive Definition kennt, ohne Namen definieren. Klingt komisch, ist aber so. Stichwort Y-Kombinator. --Daniel5Ko 00:12, 28. Nov. 2010 (CET)Beantworten

Parametername immer unwichtig?

[Quelltext bearbeiten]

Einige Programmiersprachen wie Python, Ruby, C# 4.0 oder Visual Basic unterstützen sogenannte benannte Argumente. Der Aufruf sieht dann in Python so aus:

aufruf(parameter1 = 2, paramter2 = "test")

Gehört dann nicht der Parametername doch mit zur Signatur? --mig000 18:08, 7. Dez. 2008 (CET)Beantworten

[Quelltext bearbeiten]

GiftBot (Diskussion) 01:54, 25. Dez. 2015 (CET)Beantworten