Während meines Informatik-Studiums habe ich einige funktionale Sprachen wie Prolog kennengelernt, aber jetzt mache ich nur in den letzten 10 Jahren Dinge wie C #, Ruby JavaScript und Java. Momentan erstelle ich eine Volltextsuchmaschine für einen Online-Shop und bin schon weit den "Imperativ Weg" gekommen. Aber nachdem er in einigen funktionalen Sprachen wie Haskell von Clojure gestolpert war, wurde klar, dass das funktionale Paradigma so viel besser passt und dass der imperative Weg einfach nicht das richtige Werkzeug für diesen Job ist.
Wir haben also einen Volltextindex von etwa 10 Millionen Datensätzen. Jeder Datensatz enthält im Wesentlichen ein Wortvorkommen, zusammen mit der ID und der Textposition aus dem Datensatz, aus dem es stammt.
Wenn der Benutzer einen Suchstring eingibt, wird er in einen Ausdrucksbaum geparst. Zum Beispiel ergibt die Suchzeichenkette "Transformator 100 W" etwas wie
%Vor%Hier gibt es einige zusätzliche "Intelligenz", aber das ist für diese Frage nicht von Belang.
Der Ausdrucksbaum wird dann rekursiv ausgewertet und führt zu einigen SQL-Abfragen, die bis zu 100.000 Zeilen in Form von .NET-DataTables zurückgeben können. Diese werden dann in Sätze oder Wörterbücher eingelesen und abhängig von den Prädikaten werden Kreuzungen und Vereinigungen angewendet, um alle Ergebnisse zu finden, die mit dem gesamten Suchausdruck übereinstimmen. Für die NEAR-Auswertung werden auch die Positionsindizes der gefundenen Vorkommen verglichen. Aber das alles wird dringend getan, mit vielen for-Schleifen.
Zusätzlich gibt es eine Ranking-Funktion, die die gefundenen Wortvorkommen zusammenfasst. Wörter, die nur als Präfixe oder mit Fuzzy-Matching (vom Datenbankserver) gefunden werden, erhalten niedrigere Werte als genaue Übereinstimmungen.
Für jedes resultierende Element muss ich auch eine Liste aller Wortvorkommen erhalten, die übereinstimmten, um diese Wörter in den Ergebnisseiten hervorzuheben.
Also ist der Bewertungsalgorithmus ungefähr eine Funktion wie
%Vor%Ich gebe nur einen groben Überblick hier, aber ich hoffe, Sie bekommen genug von einem Bild.
Jetzt die "realen Welt" Einschränkungen:
Da ich bei .NET bleiben muss, habe ich Clojure-CLR, F # und Scala für .NET untersucht.
Ich mag die Konzepte von Clojure sehr, aber im Moment kann ich nicht beurteilen, ob es für den Job geeignet wäre. Das Lesen von F # gab mir gemischte Gefühle, da es scheinbar alles tun möchte, während ich zu einem eher "reinen" mathematischen Ansatz für die gegebene Aufgabe tendieren würde. Aber vielleicht ist das auch mit F # möglich und mir ist das noch nicht bewusst. Ich habe mich noch nicht sehr intensiv mit Scala beschäftigt, aber es scheint gut etabliert zu sein.
Alle Einsichten wären willkommen!
Ich bin neugierig, warum Sie LINQ nicht als Option in Erwägung ziehen. Es scheint alle Ihre Kriterien zu erfüllen. Hinweis Ich habe keine Erfahrung mit Scala, daher kann ich dazu nichts sagen.
- Die gesamte Anwendung (bis jetzt) ist in C # geschrieben, daher ist eine einfache Integration mit .NET von größter Bedeutung.
- Viele Daten werden in .NET-DataTables gelesen und müssen dann ausgewertet und transformiert werden. Die Ergebnisse sollten in .NET-Typen (Wörterbücher, Sets, Arrays, was auch immer ...) enthalten sein.
Hier LINQ & gt; F # & gt; Clojure-CLR. Wenn alles bereits in C # ist, wird LINQ am einfachsten zu integrieren sein. Visual Studio-Unterstützung für Dinge wie Intellisense- und Funktionsdefinitions-Navigation scheint in einem C # -only-Programm viel besser zu sein. Calling Clojure von C # kann schrecklich sein - in der Theorie sollte es OK funktionieren, aber in der Praxis, bereit sein, verbringen verbringen Wochen herauszufinden, warum die Dinge nicht so funktionieren, wie Sie erwarten würden. Es ist wirklich so konzipiert, dass es das Beste ist. Du rufst C # von Clojure an und gehst in die entgegengesetzte Richtung. Auf der Prioritätenliste der Clojure-CLR-Entwickler steht nicht viel. Es gibt grundlegende Unterstützung, aber Sie bekommen, was Sie bekommen.
- Leistung ist von großer Bedeutung. Derzeit benötigt mein Algorithmus oft zwei Sekunden für eine Suche (nicht die sql zählen), was in Ordnung ist, aber sollte verbessert werden. Unser Server verfügt über 16 Prozessoren, so dass eine parallele Verarbeitung wünschenswert wäre. Da wir pro Sekunde etwa eine Suchanforderung erhalten und die aktuelle Implementierung single threaded ist, ist noch Prozessorzeit verfügbar.
LINQ ~ = F # & gt; Clojure. Ich habe an anderer Stelle gelesen, dass die Leistung von LINQ für die meisten idiomatisch geschriebenen Algorithmen besser als F # gezeigt werden kann, aber sie sind nah genug, dass es keine Rolle spielt. PLINQ erleichtert die Parallelität. Clojure-CLR hat mega-langsame Startzeit, und der Overhead der Laufzeit verlangsamt auch die Dinge.
- Die Sprache (und der Compiler) sollte ausgereift sein.
LINQ & gt; = F # & gt; Clojure. Um nicht zu sagen, dass F # überhaupt nicht ist, aber Visual Studio-Unterstützung hinterherhinkt, und es gibt viel mehr Produktionscode in der Welt (und viel mehr Stack-Overflow-Antworten) basierend auf LINQ als F #.
Das Lesen von F # hat mir gemischte Gefühle gegeben, da es scheint, als ob ich in der Lage wäre, fast alles zu tun, während ich zu einem "reineren" mathematischen Ansatz für die gegebene Aufgabe tendieren würde. Aber vielleicht ist das auch mit F # möglich und ich bin mir dessen noch nicht bewusst.
Keine der Sprachen ist rein rein wie Haskell, aber in Bezug darauf, wie schwierig es ist, nicht-reinen Code zu schreiben, würde ich es als LINQ & gt; Clojure & gt; F # & gt; Scala. LINQ kann nur durch das Aufrufen unreiner Methoden unrein gemacht werden. Clojure hat refs und atoms, F # alles kann als veränderbar bezeichnet werden, und Scala (nach meinem Verständnis) ist wirklich nur Java mit funktionierenden Features.
Die funktionale Funktion, die F # und Scala für sie haben, ist die Unterstützung von Sprachen für die Mustererkennung. Wo in C # Sie entweder eine Art Vererbungshierarchie oder Ketten von b? X: y-Operatoren benötigen, um Dinge funktional zu erledigen (oder wenn Sie mit einem nicht-funktionalen Ansatz gut zurechtkommen), macht das Pattern-Matching bedingte Operationen auf verschiedenen Variationen von Rohdatentypen viel prägnanter. Dies könnte bei der Berechnung von exakten vs Präfix vs Fuzzy-Match-Rankings nützlich sein, aber ab? X: y chain var alg = x.match == exact ? alg1 : x.match == prefix ? alg2 : alg3
in C # wäre in diesem einfachen Fall perfekt lesbar - wenn das Matching viel komplizierter wird als das in Sprache integrierte Muster Matching wird wertvoller.
Interessanterweise denke ich, dass der eine Aspekt Ihres Toolkits, bei dem F # sich als nützlicher erweist als LINQ, nicht die Abfrage ist, die der Name von LINQ selbst angeben sollte, sondern das Parsen Ihrer Suchzeichenfolge in eine Ausdrucksbaumstruktur. Dies ist ein Bereich, in dem sich funktionale Sprachen und Mustererkennung wirklich auszeichnen, und Add-In-Tools wie FsLex und FsYacc können Ihnen einen großen Vorsprung verschaffen.
Alles, was gesagt wurde, ich denke, die Entscheidung kommt dahin, wo Sie hingehen wollen. Wenn Sie nur Ihre Suchalgorithmen aufräumen und damit fertig sein wollen, würde ich den LINQ-Ansatz empfehlen. Aber wenn Sie Stück für Stück für das gesamte Programm in einen funktional orientierten Stil einsteigen wollen (und Ihr Unternehmen bereit ist, für die Zeit zu bezahlen, für die Sie sich verpflichten), dann schauen Sie sich vielleicht die F # an. Möglichkeit. Wie auch immer, ich würde zuerst die LINQ-Option ausführen, da dies wahrscheinlich für Sie einfacher ist, und dazu beitragen, dass Ihre F # funktional idiomatischer wird, sobald Sie diesen Pfad beginnen.
Vereinfachend, hier ist, was Sie wollen, füllen Sie einfach Ihre Funktionen für Ihre Near und Equal Fetchers und Ihre GetRank und GetStrings Funktionen, und nutzen Sie die unten
%Vor%so:
%Vor% Dies gibt Ihnen sowohl einfache Parallelisierbarkeit als auch Faulheit, so dass die Funktion GetStringsIn
nur für die Datensätze ausgeführt wird, die Sie aufnehmen (in diesem Fall die oberen 30). (Beachten Sie, dass der Selektor AND
mit einem der Beispiele IntersectAll
vereinfacht werden kann hier ).
- Die gesamte Anwendung (bis jetzt) ist in C # geschrieben, daher ist eine einfache Integration mit .NET von größter Bedeutung.
- Viele Daten werden in .NET-DataTables gelesen und müssen dann ausgewertet und transformiert werden. Die Ergebnisse sollten in .NET enthalten sein Typen (Wörterbücher, Sets, Arrays, was auch immer ...).
F # sollte eine bessere Wahl sein. Als erstklassige Sprache in Visual Studio ist die Interoperabilität von F # mit C # ziemlich gut.
- Leistung ist von großer Bedeutung. Derzeit benötigt mein Algorithmus oft zwei Sekunden für eine Suche (nicht die sql zählen), die Art ist ok, aber sollte verbessert werden. Unser Server hat 16 Prozessoren, also Parallelverarbeitung wäre willkommen. Da kommen wir auf eine Suche Anfrage pro Sekunde und die aktuelle Implementierung ist single threaded, Prozessorzeit ist noch verfügbar.
Wenn Sie davon ausgehen, dass Sie mit einer funktional ersten und unveränderbaren Implementierung beginnen, sollte es einfach sein, Ihre App zu parallelisieren. Darüber hinaus ist der asynchrone Workflow ein Segen für IO-gebundene Anwendungen wie Ihre.
- Die Sprache (und der Compiler) sollte ausgereift sein.
Ich vergleiche F # nicht mit Clojure und Scala auf JVM, aber F # ist viel reifer als Clojure CLR und Scala auf .NET. Bei der Auswahl von F # sind Sie sicher, dass Sie sich von Microsoft langfristig engagieren und von der ständig wachsenden F # -Community profitieren.
Wenn der Benutzer eine Suchzeichenfolge eingibt, wird er in einen Ausdruck umgewandelt Baum.
Sie können Ausdrucksbäume darstellen, indem Sie diskriminierte Gewerkschaften verwenden. Mit der Einführung von Abfrageausdrücken in F # 3.0 können Sie Ihre Logiken übersetzen SQL-Abfragen leicht. Sie können es sogar noch weiter vorantreiben, indem Sie eine ähnliche Abfragesprache für Ihre Domain definieren.
Das Lesen von F # gab mir gemischte Gefühle, da es so scheinen wollte in der Lage, fast alles zu tun, während ich zu einem mehr neigen würde "reiner" mathematischer Ansatz für die gegebene Aufgabe. Aber vielleicht ist das so auch mit F # möglich und mir ist das noch nicht bekannt.
F # 3.0 führt Anbieter ein , um Benutzern den Zugriff strukturierte Daten in typsicherer Weise; Vielleicht möchten Sie lesen Dieses "F # 3.0 - Information Rich Programming" Video für weitere Einblicke. Wenn Sie F # als Programmiersprache für das Data Mining verwenden möchten, habe ich eine verwandte Frage gestellt und ziemlich gute Antworten erhalten hier .
Das heißt, Ihre ersten Gefühle zu F # sind möglicherweise nicht korrekt. Aus meiner Erfahrung können Sie immer so nah an der funktionalen und unveränderlichen Seite bleiben, wie Sie wollen. Da Sie bereits eine interessante Anwendung haben, schlage ich vor, sich die Hände schmutzig zu machen, um zu wissen, ob F # die Sprache ist zu deinem Zweck.
UPDATE:
Hier ist ein F # -Prototyp, der die Idee demonstriert:
%Vor%