![]() |
|
|
|||||||
| Softwareentwicklung Allgemeine Softwareentwicklung - Andere Programmiersprachen, Regex, OOP, Design Patterns |
|
|
|
Themen-Optionen | Thema durchsuchen | Ansicht |
| #1 (permalink) | |||||||||||||||||||
|
Stammbenutzer
Halbes Gigabyte
Registriert seit: 06.04.2005
Fachbeiträge: 5.437
Blog-Einträge: 15
Abgegebene Danke: 193
Erhielt 686 Danke für 563 Beiträge
|
Als Ausgangspunkt soll einmal dieses Zitat dienen:
Das es gut ist, in seinem Code Strukturen zu entdecken, die sich verallgemeinern lassen, sollte eigentlich klar sein: Jede "entdeckte" Abstraktion ist eine Chance, wiederverwendbaren Code entweder zu schreiben oder zu nutzen. Ein Monoid ist ein sehr einfaches Konzept: Wir haben eine assoziative Operation # (also a # (b # c) == (a # b) # c) ) mit einem neutralen Element e (dass bei Veknüpfung mit einem anderen Element dessen Wert nicht ändert). Monoide sind z.B. die natürlichen Zahlen bezüglich der Addition und Multiplikation, aber auch die Verkettung von Strings oder Listen(*). Hat man diese Abstraktion einmal erkannt, kann man z.B. eine List<T> zu einem einzelnen Wert T "eindampfen" (oder "falten"), egal welche Art von Monoid über T man benutzt. Eigentlich eine praktische Sache.Also: Wieso lassen sich Programmierer so leicht von mathematisch angehauchten Namen abschrecken? Irgendwie muss man die Strukturen ja nennen, und es wäre Blödsinn, eine eigene Nomenklatur zu verwenden, wenn es schon eine allgemein akzeptierte, konsistente Benennung gibt. Wieso werden so schnell die Scheuklappen aufgesetzt, wenn ein Wort wie "Monoid", "Funktor", "Arrow" oder dreikreuzederherrseibeiuns "Monade" fällt? (*) Mehr Beispiele: Monoid (Functional Java 3.0) Geändert von Landei (05.09.2011 um 08:14 Uhr) |
||||||||||||||||||
|
|
|
||||||||||||||||||
| #2 (permalink) | |
|
Stammbenutzer
Floppy Disc
Registriert seit: 09.09.2003
Fachbeiträge: 821
Abgegebene Danke: 39
Erhielt 98 Danke für 76 Beiträge
|
Ich glaub es geht da weniger um das Wort "Monoid", als vielmehr um das Verstaendnis dieses Konstrukts. Ich hab mir das ne Weile angesehen, aber als Scala-Nicht-Kenner versteh ich ueberhaupt nicht was das sein soll und wozu es gut ist. Dieser Effekt, dass man einige Statements ohne tieferes Wissen ueber die Sprache auch nicht annaehernd verstehen kann, macht Scala in gewisser Weise einzigartig.
Und ich rede jetzt nicht von der Syntax oder der Formatierung (man kann auch Java so kryptisch schreiben, dass es niemand versteht), sondern von der Semantik. Die andere Sache ist, dass der normale Entwickler wohl eher selten das Verlangen hat, einen neuen Monoiden zu programmieren. Meisten rechnet man mit Zahlen, iteriert ueber Listen und konkatiniert Strings. Mehr braucht man selten und damit fehlt auch das Verstaendnis, wieso man nun mit Scala ploetzlich mit Monoiden, Funktoren oder Faltungen rummachen muss. |
|
|
|
| #3 (permalink) | ||||||||||||||||
|
Stammbenutzer
Halbes Gigabyte
Themenstarter
Registriert seit: 06.04.2005
Fachbeiträge: 5.437
Blog-Einträge: 15
Abgegebene Danke: 193
Erhielt 686 Danke für 563 Beiträge
|
Wie gesagt soll das hier keine Scala-Diskussion werden, deshalb nur soviel: Der obige Code ist ein typisches Beispiel für das Scala-Typklassen-Pattern. Hier ein praktisches Beispiel: Algorithmically challenged: Type Class pattern example
In Java werden sogar die einfachsten Abstraktionsmöglichkeiten verpasst: Wieso hat man List.size() , String.length() und array.length ? Wieso implementiert String nicht Iterable<Character> ? Wieso implementiert StringBuilder nicht List<Character> ? Die Liste ließe sich endlos fortführen. Das Problem ist, dass man einerseits zehn verschiedene Wege (oder Methodennamen) lernen muss, um prinzipiell das Gleiche zu tun, aber andererseits die vorhandenen Methoden deshalb nicht so flexibel sind, wie sie sein könnten. Bin ich der einzige, der das doof findet?
|
|||||||||||||||
|
|
|
|||||||||||||||
| #4 (permalink) | |
|
Premium-Benutzer
Registriert seit: 07.02.2004
Fachbeiträge: 7.817
Abgegebene Danke: 10
Erhielt 152 Danke für 74 Beiträge
|
Ich möchte nicht, dass jede Klasse 20 Interfaces implementiert und 300 Methoden hat... Bei Eiffel wurde das so gemacht (ein Fenster ist ein Rectangle, und eine Liste von Componenten, und und und). Resultat: flexibel, aber so extrem unübersichtlich dass es praktisch nicht zu gebrauchen ist.
|
|
|
|
| #5 (permalink) | |||||||||||||||||||
|
Stammbenutzer
CD-R 74
Registriert seit: 16.09.2006
Fachbeiträge: 6.762
Abgegebene Danke: 28
Erhielt 529 Danke für 496 Beiträge
|
Wie kommst du auf 10
__________________
Welches ist das beste Buch für Anfänger? Das: Java von Kopf bis Fuss Nach den ersten Schritten? Das: Der Weg zum Java-Profi |
||||||||||||||||||
|
|
|
||||||||||||||||||
| #6 (permalink) | |
|
Java-Forum Team
Moderator
Registriert seit: 06.01.2007
Fachbeiträge: 16.760
Abgegebene Danke: 0
Erhielt 1.640 Danke für 1.485 Beiträge
|
NEIN. Viele dieser Punkte sind mir auch schon aufgefallen. Ein Beispiel, das in die gleiche Kategorie fällt, sind StringBuilder und StringBuffer: Für ein paar 'synchronized's eine neue Klasse?!
![]() Ich glaube auch, dass viele Leute sich lange über bestimmte Strukturen und Ansätze Gedanken machen. Dann kommen sie zu irgendeinem Ergebnis, und setzen es um. Aber ein ähnliches oder sogar besseres Ergebnis hätte man erreichen können, wenn man sich pragmatischer an die Vorgaben der rein mathematischen Beschreibung gehalten hätte. Mathematik ist eine Sprache für gute Beschreibungen, und anscheinend wird viel zu selten erkannt, dass die Algebra sie eigentliche formale Grundlage der Objektorientierten Programmierung ist. Oder anders: Man muss vielleicht nicht die Definition von "Monoid" auswendig wissen (da könnte man gleich noch nach Gruppe, Ring und Körper fragen - darum geht es ja nicht) aber wer sich "Informatiker" schimpft, sollte schon wissen, dass das eine (einfache) algebraische Struktur ist - und wenn nicht, hat derjenige IMHO viel nachzuholen... Ich fand z.B. sowas wie org.jscience.mathematics.structure (JScience 5.0-SNAPSHOT API) vom Ansatz her toll, aber erstens nicht konsequent und systematisch genug umgesetzt, und zweitens nicht so "mächtig", wie es sein könnte, wenn dieser Aspekt systematischer in der Standard-API berücksichtigt worden wäre. Aber ich nehme an, dass dein Einwand sich nicht nur auf die existierenden, schon mathematisch definierten Strukturen an sich bezog, sondern, wie im Titel angedeutet, um allgemeine Abstraktionen - also die Definition eigener Algebren. Über ähnliche Aspekte hatte ich mir auch schon oft Gedanken gemacht. Und ich habe immer Angst, dass, wenn man mögliche Abstraktionen ausnutzt, der entstehende Code (primär von anderen) nicht mehr so gut nachvollzogen werden kann. Man könnte sagen, dass ein weiterer Grundgedanke der OOP ja der ist, irgendwelchen Dingen Namen zu geben. Und wenn diese Namen wegen "zu viel Abstraktion" wegfallen, und jeder Typ nur noch eine lose Ansammlung von Mengen, Funktoren, Tupeln, Gruppen und Prädikaten ist, erleichtert das das Verständnis nicht unbedingt. Etwas pauschalisiert könnte man wohl sagen, dass es einen Trade-Off gibt, zwischen der Flexibilität und Allgemeingültigkeit, die die Mathematik an sich innehat, und der einfachen Verbindung zwischen Code und Objekten in der "realen" Welt. Aber ich versuche zumindest eher in Richtung der allgemeineren Beschreibung zu gehen. Wenn jemand anderes das dann nicht mehr versteht... tja... |
|
|
|
| #7 (permalink) | |
|
Stammbenutzer
Halbes Gigabyte
Themenstarter
Registriert seit: 06.04.2005
Fachbeiträge: 5.437
Blog-Einträge: 15
Abgegebene Danke: 193
Erhielt 686 Danke für 563 Beiträge
|
Das reicht auch schon. Ohne nachzuschlagen: Heißt es
File.length() oder File.size() ? Heißt es javax.swing.text.Document.length() oder Document.size() ? Reingefallen, es ist Document.getLength() !
Geändert von Landei (05.09.2011 um 09:51 Uhr) |
|
|
|
| #9 (permalink) | |
|
Java-Forum Team
Moderator
Registriert seit: 13.11.2005
Fachbeiträge: 31.675
Abgegebene Danke: 0
Erhielt 2.570 Danke für 2.531 Beiträge
|
und dann auch getSize() in ListModel
![]() aber das ist kein rechter Angriffspunkt, das sind zum Teil separate Libraries, unabhängig von der Sprache, oder nach Jahren von verschiedenen Programmierer-Generationen hinzugefügt, dass dort nicht alles verregelt ist ist eine gewisse Entspannung, es hilft nix, komplizierte Neuerungen haben es immer schwer
__________________
Hansa wird Meister. |
|
|
|
| #10 (permalink) | |||||||||||||||||||
|
Stammbenutzer
Halbes Gigabyte
Themenstarter
Registriert seit: 06.04.2005
Fachbeiträge: 5.437
Blog-Einträge: 15
Abgegebene Danke: 193
Erhielt 686 Danke für 563 Beiträge
|
Iterable implementiert, wo das sinnvoll wäre. In Haskell würde eine Bibliothek, die Instanzen für wichtige Typklassen definieren könnte, das aber nicht tut, dafür mit Sicherheit kritisiert und u.U. nicht akzeptiert werden. Diese Einstellung fehlt mir bei den imperativen Sprachen.Ein Grund dafür ist, ist, dass durch Implementierung bekannter Interfaces o.ä. auch leichter zu sehen ist, was eine Klasse tut. Wenn ich z.B. eine add-Methode habe, ändert sie dann das Objekt, oder liefert ein neues zurück? Und nein, das kann man nicht sofort am Rückgabewert sehen (man denke z.B. an StringBuilder.append ). Eine Abstraktion erfüllt also auch eine wichtige Dokumentations-Funktion.
|
||||||||||||||||||
|
|
|
||||||||||||||||||
| #11 (permalink) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Stammbenutzer
Viertel Gigabyte
Registriert seit: 22.04.2007
Fachbeiträge: 3.668
Abgegebene Danke: 17
Erhielt 36 Danke für 36 Beiträge
|
Nja, 'tschuldigung: ich wollte in dem Beispiel mit einer 0 anfangen, und alle Elemente einer Liste mit + addieren, dazu brauchte ich eben eine Struktur mit 0 und +, soll ich die jetzt "GridBagLayout" oder "Helmut" nennen, damit's vertrauter klingt?
![]()
Kaum entwirft jemand eine Sprache, wo sowohl die fürchterlichen Bibliothek-Innereien, als auch die benutzerfreundlichen DSL's implementiert werden können, geht das Geheule los, es sei doch "Alles so kompliziert!", und "Wie soll ich denn bloß nach Mallorca fliegen, ohne eine Flugzeugturbine zusammenbauen zu können?!". Scala vereint alles: es ist gut für die Benutzer der Bibliotheken, und es ist gut für die Designer der Bibliotheken. Wenn man sich als Benutzer versteht, dann sollte man sich aus dem Design der Bibliotheken einfach erstmal raushalten. Oder es einfach lernen: es ist nämlich ein riesen Spaß. ![]()
Code:
sum(seq) sum(seq) -methode neuimplementieren zu müssen. Und da fängt man schon an, sich Gedanken über die richtige Abstraktionen zu machen.Warum nicht? Das ist doch Frage der Dokumentation, nicht die Frage der zu komplexen Syntax. Ist es denn besser, die 300 methoden jedes mal neu zu implementieren?
1) In der Mathematik rechnet man oft mit Objekten herum, von den anfangs nicht ganz klar ist, welche Struktur sie eigentlich tragen: ist beispielsweise Z/(8220625327)Z ein Körper, oder lediglich ein Ring mit Nullteilern? Soll man elemente solchen Dings als "Ring" oder "Field" initialisieren, um damit rumzurechnen? Während man damit rumrechnet, führt man gerne (beweisbar gültige) Teilungen durch, auch wenn man im Ring ist, also müsste das Ring-interface eigentlich auch inverse beinhalten, aber wodurch unterscheiden sich dann Ringe von Körpern bei der Implementierung? 2) Um irgendwelche strukturierten Teilmengen zu erkennen, ist das Typsystem nicht mächtig genug. Beispielsweise bilden invertierbere quadratische Matrizen gleicher Größe eine wunderbare Gruppe, aber allgemeine matrizen kann man nicht mal vernünftig multiplizieren: krampfhafte Versuche, da irgendeine mathematisch korrekt definierte Struktur drüberzuziehen sehen nicht gut aus. 3) Hirnlose Skolemisierung der mathematischen Definitionen führt nicht zu brauchbaren schnittstellen. Beispiel Graphen: Mathematisch ist ein Graph G = (V, E) eine menge von Knoten blah blah blah... Es ist aber schwachsinnig, das ganze wie folgt zu einem interface zu machen: Code:
interface Graph[V, E]{
Collection[V] getNodes()
Collection[E] getedges()
}
Man muss da also schon mit viel Gefühl rangehen, wie genau versuche ich herauszufinden.
Geändert von 0x7F800000 (05.09.2011 um 13:40 Uhr) |
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
| #12 (permalink) | |||||||||||||||||||
|
Stammbenutzer
Floppy Disc
Registriert seit: 09.09.2003
Fachbeiträge: 821
Abgegebene Danke: 39
Erhielt 98 Danke für 76 Beiträge
|
Code:
def sum(initial_value, seq)
seq.inject(initial_value) do |result, s|
result + s
end
end
|
||||||||||||||||||
|
|
|
||||||||||||||||||
| #13 (permalink) | |
|
Stammbenutzer
Halbes Gigabyte
Themenstarter
Registriert seit: 06.04.2005
Fachbeiträge: 5.437
Blog-Einträge: 15
Abgegebene Danke: 193
Erhielt 686 Danke für 563 Beiträge
|
Geht bestimmt, ist aber für ernsthafte numerische Aufgaben zu langsam. Scala hätte als Äquivalent strukturelle Typen anzubieten ("für alle Typen, die eine Methode foo(Bar) haben, tue...", siehe Pimp my Library eSCALAtion Blog ), die aber auch nicht gerade für ihre Performance berühmt sind.
Geändert von Landei (05.09.2011 um 14:07 Uhr) |
|
|
|
| #14 (permalink) | |||||||||||||||||||
|
Stammbenutzer
Viertel Gigabyte
Registriert seit: 22.04.2007
Fachbeiträge: 3.668
Abgegebene Danke: 17
Erhielt 36 Danke für 36 Beiträge
|
Code:
sum([[1,2,3],[4,5,6],[7,8,9]]) //liefert [1,2,3,[4,5,6],[7,8,9]] statt [12,15,18] 2) das "klassische OOP" strebte ursprünglich danach, daten und methoden zu vereinen. Das macht aber nicht immer unbedingt Sinn. Ich möchte vielleicht mit Double-daten rumrechnen, aber einmal mit der üblichen Körperstruktur, und anderes mal wie in einem tropischen ring mit Operationen (max, +) statt (+, *). Eher denkbar: man möchte mit longs rumrechnen, allerdings nicht mit üblichen Z/(2^64)Z-Ring, sondern in einem Z/pZ-Körper. Da man nur bei statisch typisierten Sprachen so etwas wie implizite paremeter übergeben kann (bei dynamisch typisierten weiß der kompiler ja nicht, was verlangt ist), müsste man die Struktur explizit übergeben. Das mag bei so einfachen methode gehen, wird aber bei komplizierteren aufgaben mir 2-3 Strukturen auf Dauer evtl. stressig. 3) Für mich eigentlich das wichtigste: dank der statischen Typisierung kann man den scala-compiler dazu zwingen, aus einfachen Formeln komplizierte Algorithmen zur compile-zeit zusammenzubauen. Beispiel: Seien A, B, C, D... matrizen Wenn man sowas schreibt: Code:
val C = A * B val Z = C * D Code:
val Z = evalInRowMajor(C)*convertToColMajor(D) Das fehlen dieser Fähigkeit merkt man schon bei deinem Beispiel: bei deiner version muss man den startwert mit übergeben, weil der compiler eben die Lücken mit dem passenden Struktur nicht ausfüllen kann. 4) [EDIT] Ok, Performance habe ich hier gar nicht erwähnt: klar, um etwas bei diesen ganzen Skriptsprachen schnell zu machen, muss man es in C/C++ schreiben. Wenn ich den s***** eh in C/C++ schreiben muss, wozu brauch ich dann die blöden Skriptsprachen überhaupt? Geändert von 0x7F800000 (05.09.2011 um 14:36 Uhr) |
||||||||||||||||||
|
|
|
||||||||||||||||||
| #15 (permalink) | |||||||||||||||||||
|
Java-Forum Team
Moderator
Registriert seit: 06.01.2007
Fachbeiträge: 16.760
Abgegebene Danke: 0
Erhielt 1.640 Danke für 1.485 Beiträge
|
Es geht ja nicht um ein Computeralgebrasystem, das genau für solche Dinge da ist, sondern nur darum, existierende Strukturen auszunutzen. Und mit Monioden, Gruppen, Ringen und Körpern hat man schon den überwiegendsten Teil davon abgedeckt - die Herausforderung würde dann nicht darin bestehen, auch ja unbedingt noch den Quasikörper und den Integritätsbereich einzubauen, oder zu Erkennen, dass diese-und-jene Struktur mit einer zusätzlichen 'null' nun zu dieser-und-jender Struktur wird, sondern darum, diese wenigen, wichtigen Strukturen konsequent und gut (d.h. "viele Vorteile bringend") umzusetzen.Das Beispiel des Graphen ist aber schon fast klassisch, und darüber (und einige andere Aspekte, die hier im Thread diskutiert werden - deswegen ist dieser Backlink vielleicht sogar ganz praktisch) hatte ich schon philosophiert, als ich den durch immer weiter gehende (speziell auch mathematische) Abstraktion drohenden Abstraktions-Overkill mal angesprochen hatte. Ich fände, dass es durchaus einen Haufen Vorteile hätte, einen Graphen als Tuple<Set<Object>, Set<Tuple<Object,Object>>> g; zu beschreiben. Aber wenn das jemand sieht, der... (neutral formuliert) "eine andere Einstellung zu solchen Dingen" hat, und entweder das verwenden kann, oder stattdessen Graph g; schreiben kann, wird er sich tendenziell eher für letzteres entscheiden. Leicht OT, weil ganz spezifisch darauf bezogen: Was wäre an dem angedeuteten Interface denn so "falsch" oder "unbrauchbar"? Abgesehen davon, dass man Set#size u.U. recht schwer implementieren kann, hat man durch die Interface an sich ja immernoch die Möglichkeit, dort vieles z.B. "lazy" zu implementieren, und sinngemäß einen Iterator beim Aufruf von "next" halt eine Webseite aufrufen zu lassen (rein theoretisch). Anders formuliert: "Ein Graph IST EIN Tupel aus einer Menge von Knoten und einer Menge von Kanten..." - wenn man etwas anderes implementiert, dann IST das - polemisch formuliert - eben kein Graph mehr.... |
||||||||||||||||||
|
|
|
||||||||||||||||||
| #16 (permalink) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Stammbenutzer
Viertel Gigabyte
Registriert seit: 22.04.2007
Fachbeiträge: 3.668
Abgegebene Danke: 17
Erhielt 36 Danke für 36 Beiträge
|
![]()
Code:
type Graph[V] = (Set[V], Set[(V, V)]) ![]() Allgemein bei Interfaces muss man irgendwie darauf achten, dass man nicht ins unendlich abstrakte & völlig unbrauchbare abdriftet. Ich bemühe mich deshalb darum, bei Traits das richtige Gleichgewicht zwischen vorgeschriebenen Methoden und gebotener Funktionalität zu halten. Einfach nur Tausend Interfaces zu liefern, an die sich der Benutzer zu halten hat, ohne dass es ihm was nützt, ist zum Scheitern verurteilt, auch wenn diese ganzen Interfaces aus algebraischer Sicht sauber definiert sind.
1) tankstelle in der Nähe suchen, ohne die ganze Weltkarte runterzuladen 2) kliquen im engen Freundeskreis bei facebook suchen, ohne ganzes facebook runterzuladen 3) backtrackings aller art auf fürchterlich großen Graphen (es gibt sehr viele Graphen, die zeitlich in unsere 4D-Welt gut reinpassen, aber niemals zur selben Zeit im Speicher Platz finden können) 4) Untergruppen-zugehörigkeits probleme, Rubic's cube lösen o.ä... bei all diesen problemen macht es keinen Sinn, die Aufzählung aller Knoten zu verlangen. Natürlich gibt es Graphen, die alle ihre Knoten kennen und Algorithmen, die das brauchen. Aber das ist dann schon ein speziellerer Unter-Trait. Auf den Punkt gebracht: mathematische Definition abschreiben != die algorithmisch entscheidenenden Eigenschaften erkennen Zum schreiben der Beweise benötigt man das erstere. Zum schreiben des Codes benötigt man das letztere. Geändert von 0x7F800000 (05.09.2011 um 15:21 Uhr) |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #17 (permalink) | |
|
Java-Forum Team
Moderator
Registriert seit: 06.01.2007
Fachbeiträge: 16.760
Abgegebene Danke: 0
Erhielt 1.640 Danke für 1.485 Beiträge
|
OK, zugegeben, die Frage was daran "unbrauchbar" ist, war ein bißchen rethorisch
Tatsächlich braucht man die reine Aufzählung der Knoten nicht oder selten. (Auch wenn, nur weil dort "Set<V>" steht, das ja nicht heißen muss, dass die Knoten ALLE im Speicher liegen - es kann ja eine quasi-implizite Beschreibung der Knoten sein). Aber es stimmt schon: Die ALLERmeisten Graphenalgorithmen brauchen massivst genau eine Funktion, nämlich: "Gibt mir alle Kanten, die an dem Knoten hängen" (Die Differenzierung zwischen ein- und ausgehenden Kanten, oder die darauf aufbauend in gleicher asymptotischer Zeit zu erreichender Suche nach allen Nachbarn mal außen vor gelassen). Für praktische Zwecke braucht man also IMMER noch eine Methode wie Iterable<Edge> getEdges(Vertex v); Sowas schreibt sich sowohl steng mathematisch als auch in einem Graph-Interface (zumindest in der naivsten Form) recht leicht hin - sei jetzt mal egal, ob mit "Edge" oder "Tuple<V,V>". Es ist aber nicht inheränter Teil der Definition eines Graphen. Es kann theoretisch auf Basis des Graphen in seiner angedeuteten, minimalistisch-abstraktesten Form leicht berechnet werden: Über alle Kanten gehen, und schauen, ob 'v' einer der Endpunkte ist - mit einer einfachen Utility-Methode. Und es müßte rein strukturell deswegen nicht Teil eines Graph-Interfaces sein, sondern wird nur aus Effizienzgründen mit dort aufgenommen. Warum? Weil der Graph eben üblicherweise sowas wie eine Map<Vertex, List<Edge>> speichert, in der diese Information leicht nachgesehen werden kann. Und das ist ... doch eigentlich nur eine Funktion, die Vertices auf Listen von Edges abbildet. Eigentlich wäre es doch schön, wenn man diese Information dann nicht als höchst-spezifische Methode in einer höchst-spezifischen Graph-Klasse beschreiben würde, sondern eben genau als Function<V, Set<Tuple<V,V>>> edgesOfVertices; Damit könnte man all die magischen Sachen machen, z.B. diese Funktion auf triviale Weise zu einer Funktion erweitern, die einen Knoten auf seinen Grad abbildet, oder auf die Menge seiner Nachbarknoten - alles Dinge, die oft praktisch sind, und durch so ein Konstrukt, dank der Abstraktion, "aus dem (fast) nichts heraus" von ganz alleine bekommt. Aber das führt dann, konsequent weitergedacht, eben zum angedeuteten Overkill, wo man nur noch mit Sets, Tuples und Functions rumhantiert, und durch reines Lesen nicht mehr erkennbar ist, ob ein Programm eine rohe Breitensuche in einem Graphen durchführt oder die Bestellannahme eines Pizzaservices regelt Wie im anderen Thread (und oben) schon angedeutet geht damit ja eine der Kernideen von OOP verloren, nämlich dass Strukturen sprechende (und vereinfacht gesagt: aus der realen Welt stammende) Namen haben. Ich denke, speziell bei sowas abstrakt-mathematischem wie Graphen ist das nicht so schlimm, und die Vorteile überwiegen deutlich, zumal um die allgemeinen Strukturen ja immernoch spezifischere Business-Strukturen drumrumgewickelt werden können... aber es ging ja ursprünglich um die allgemeine Frage, wie viel Abstraktion man einem Programmierer zumuten oder abverlangen kann ![]() Meine ersten Gehversuche in Scala waren übrigens ... der Versuch, einer Graph-Klasse weil ich dachte, dass es erstens eine tolle Übung wäre, und zweitens sowas damit ja aus den angedeuteten Gründen und in der angedeuteten Form total cool und allgemein umsetzbar sein müßte - und ich bin kläglich gescheitert ... da braucht's wohl viel, viel mehr Übung und Versiertheit, um da etwas in diesem Sinne vernünftiges machen zu können...
Geändert von Marco13 (05.09.2011 um 15:59 Uhr) |
|
|
|
| #18 (permalink) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Stammbenutzer
Viertel Gigabyte
Registriert seit: 22.04.2007
Fachbeiträge: 3.668
Abgegebene Danke: 17
Erhielt 36 Danke für 36 Beiträge
|
Code:
trait Graph[V]{
// abstrakt
def neighbors(x: V): Iterable[V]
// das evtl überschreiben
def sccRepresentants: Iterable[V] = new Set[V]()
// alles andere lässt sich ja durch abstrakte methoden ausdrücken
def degree(x: V) = neighbors(x).size
// ... 150 weitere nützliche methoden, etwa:
// search(start, predicate):X
// aStar(heuristic, predicate):X
// bfs(x, predicate)
// ...
}
Code:
implicit def functionToGraph[V](f: V => Iterable[V]) = new Graph[V]{
def neighbors(x: V) = f(x)
}
Code:
val nodes = List("Marco13", "Landei", "L-ectron-X")
val firstNeighbors = for(n <- nodes) yield g.neighbors(name).head
val separateFunction = (s: String) => g.neighbors(s)
Weißt Du was, irgendwie habe ich das Gefühl, als ob du durch das Fehlen der higher-order-functions und closures in Java zu irgendwelchen merkwürdigen Workarounds abdriften würdest, als ob du geistig schon in einer funktionalen Sprache denken, aber physisch noch in Java coden würdest Vor dem Umstieg auf Scala ging es mir so: ich habe plötzlich angefangen, haufenweise Klassen wie "Tuple, Pair, Function, LazyMappedSequence, MappedFunction" usw. in java zu implementieren und sie an jeder Stelle zu benutzen, bis mir aufgefallen ist, dass ich eigentlich kein Java mehr schreibe, sondern lediglich java-syntax zur simulation einer funktionalen Sprache missbrauche ![]() [EDIT] *im tagebuch rumblätter* ![]() Genau habe ich damals folgende Hilfsmittel in Java implementiert: MapFunction, MappedIterable, MultiIterable, EmptyIterator, MappedIterator, MultiIterator, Function, Identity, Inclusion, Memoization, Pair, Square, VisitorProcess, IntRange, LongRange, LongCounter... also lauter so Sachen, die irgendwie Funktionales Programmieren imitieren sollten
Geändert von 0x7F800000 (05.09.2011 um 17:19 Uhr) |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #19 (permalink) | ||||||||||||||||||||||||||||||||||
|
Java-Forum Team
Moderator
Registriert seit: 06.01.2007
Fachbeiträge: 16.760
Abgegebene Danke: 0
Erhielt 1.640 Danke für 1.485 Beiträge
|
Dass es verschiedene äquivalente mathematische Beschreibungen geben kann, die bei "direkt-naiver" Umsetzung unterschiedlich gut implementieren lassen (oder auch abhängig von der Art des Graphen unterschiedlich effizient sind - z.B. bei "dünn besetzen" Graphen vs. vollständige Grapen) kommt zu dem ganzen noch dazu. Aber mit Interfaces und abstrakten Beschreibungen hält man sich für die Implementierung ja zum Glück gerade diese Türen so lange wie möglich offen.
daraus eine "List<Vertex>" machen: AbstractList erweitern, und die 1,2 abstrakten Methoden auf die beiden vorhandenen umbiegen und gut. Aber ich hätte einen Vorteil der allgemeinen Beschreibung (GROB im Sinne von oder gar das schon angedeutete Tuple<Collection<Vertex>, ...>, an dem man gar nicht mehr sieht, dass es ein Graph ist!) eben darin gesehen, dass man dieses Einwickeln und Umbiegen gar nicht mehr braucht.
Ich will und brauche mehr funktionale Aspekte, und merke selbst, dass ich tendenziell immer häufiger anonyme Implementierungen einzelner Interfaces für solche "Umbiege-Operationen" verwende. Eigentlich hatte ich gehofft, dass das bei Scala weniger notwendig sein könnte, aber deinen Ausführungen nach scheint es lediglich besser... "antizipiert" zu sein Ich finde den Einstieg in Scala trotzdem schwer... (mal schauen, was in Yes, Scala is hard so neues steht ). Hatte auch schonmal Goovy ins Auge gefasst. Dazu kommt , dass ich gerne von anfang an etwas sinnvolles machen würde, man aber vermutlich sicher sein kann, dass man von allem, was man anfangs in Scala schreibt, wenige Monate später erkennt, was für ein crap es eigentlich war Die zur Verfügung stehenden Mittel gut zu nutzen erfordert so viel Übung, dass vorher erstmal viel Zeit vermeintlich verbrannt wird...
|
|||||||||||||||||||||||||||||||||
|
|
|
|||||||||||||||||||||||||||||||||
| #20 (permalink) | ||||||||||||||||
|
Stammbenutzer
Viertel Gigabyte
Registriert seit: 26.07.2011
Fachbeiträge: 2.886
Abgegebene Danke: 76
Erhielt 600 Danke für 590 Beiträge
|
__________________
http://www.winfonet.eu |
|||||||||||||||
|
|
|
|||||||||||||||
|
| Themen-Optionen | Thema durchsuchen |
| Ansicht | |
Ähnliche Themen
|
||||
| Thema | Autor | Forum | Antworten | Letzter Beitrag |
| Bei TreeMap, die Klammer ausblenden ? | Javamoto | Allgemeine Java-Themen | 1 | 02.02.2011 12:44 |
| Checkboxen Namen setzten über Parameter => Abfragen? | Mulan | AWT, Swing, JavaFX & SWT | 2 | 28.11.2010 10:38 |
| Namen soriteren | Java Basics - Anfänger-Themen | 19 | 28.08.2007 22:56 | |
| Kann man den Namen einer Variable in ein String Konvertieren | foxcomp | Java Basics - Anfänger-Themen | 2 | 24.01.2007 22:09 |
| selectabfrage um einen eingegeben namen rauszufinden | jinx | Datenbankprogrammierung | 3 | 16.06.2004 12:34 |
| Lesezeichen |
|
|