LINQ をオプションとして検討していない理由が知りたいです。すべての基準を満たしているようです。私は Scala の経験がないので、それについてコメントすることはできません。
- アプリケーション全体 (これまで) は C# で記述されているため、.NET との簡単な統合が最も重要です。
- データのロードは .NET-DataTables に読み込まれ、評価および変換する必要があります。結果は、.NET 型 (辞書、セット、配列など) に含まれている必要があります。
ここでは、LINQ > F# > Clojure-CLR です。すべてが既に C# にある場合は、LINQ が最も簡単に統合できます。インテリセンスや関数定義ナビゲーションなどの Visual Studio のサポートは、C# のみのプログラムではるかに優れているようです。C# から Clojure を呼び出すのは恐ろしいことです。理論的には問題なく動作するはずですが、実際には、期待どおりに動作しない理由を理解するために何週間も費やす覚悟をしてください。それは本当にトップレベルのものになるように設計されています。Clojure から C# を呼び出す場合、逆方向に進むことは、Clojure-CLR 開発者の優先順位リストの上位にはありません。基本的なサポートがありますが、得られるものは得られます。
- パフォーマンスは非常に重要です。現在、私のアルゴリズムは検索に 2 秒かかることがよくあります (SQL はカウントしません)。これは問題ありませんが、改善する必要があります。私たちのサーバーには 16 個のプロセッサがあるため、並列処理は大歓迎です。1 秒あたり約 1 件の検索要求があり、現在の実装はシングル スレッドであるため、プロセッサ時間はまだ利用可能です。
LINQ ~= F# > Clojure. ほとんどの慣用的に記述されたアルゴリズムでは、LINQ のパフォーマンスが F# よりもわずかに優れていることが示されていることを他の場所で読んだことがありますが、それらは問題にならないほど十分に近いものです。PLINQ を使用すると、並列処理が容易になります。Clojure-CLR の起動時間は非常に遅く、実行時のオーバーヘッドも速度を低下させます。
- 言語 (およびコンパイラ) は成熟している必要があります。
LINQ >= F# > Clojure. F# がまったく未熟であるとは言いませんが、Visual Studio のサポートは少し遅れており、世界には F# よりも LINQ に基づいたプロダクション コード (およびスタック オーバーフローの回答がはるかに多い) が存在します。
F# について読むと、複雑な気持ちになりました。与えられたタスクに対して、より「純粋な」数学的アプローチを採用する傾向があるのに対し、F# はほぼすべてのことを実行できるように思われるからです。しかし、おそらくそれはF#でも可能であり、私はまだそれを認識していません.
Haskell のように純粋で純粋な言語はありませんが、非純粋なコードを書くことがどれほど難しいかという点で、LINQ > Clojure > F# > Scala にランク付けします。LINQ は、不純なメソッドを呼び出すことによってのみ不純にすることができます。Clojure には ref と atom があり、F# は何でも変更可能に指定できます。Scala (私の理解によると) は実際には、機能的な機能がボルトで固定された単なる Java です。
F# と Scala の両方が備えている機能的な機能は、パターン マッチングの言語サポートです。C# では、機能的に物事を行うためにある種の継承階層または b?x:y 演算子のチェーンが必要な場合 (または、非機能的なアプローチで問題がない場合は if/else)、パターン マッチングは異なる条件付き操作を行います。生データ型のバリエーションははるかに簡潔です。これは、完全一致 vs 接頭辞 vs あいまい一致のランキングの計算に役立つ可能性がありvar alg = x.match == exact ? alg1 : x.match == prefix ? alg2 : alg3
ますが、この単純なケースでは、C# の ab?x:y チェーンは完全に判読可能です。言語統合パターン マッチングがより複雑になるのは、マッチングがより複雑になるときです。貴重。
興味深いことに、F# が LINQ よりも有用であることが証明されるツールキットの 1 つの側面は、クエリではなく、LINQ の名前自体が処理できることを示していると思いますが、検索文字列を式ツリーに解析します。 これは、関数型言語とパターン マッチングが非常に優れている分野の 1 つであり、FsLex や FsYacc などのツールを追加すると、大きな有利なスタートを切ることができます。
そうは言っても、決定はあなたがどこに行きたいかによると思います。検索アルゴリズムをクリーンアップして完了したい場合は、LINQ アプローチをお勧めします。しかし、プログラム全体を少しずつ機能指向のスタイルにしたい場合 (そして、あなたの会社は、あなたがコミットしている時間に対して喜んでお金を払ってくれるでしょう)、F# を見てください。オプション。いずれにせよ、最初に LINQ オプションを実行します。その方がより簡単であり、そのパスを開始すると、F# がより機能的に慣用的になるようにガイドするのに役立ちます。
簡単に言うと、これがあなたが望むものです。Near および Equal フェッチャーの関数と、GetRank および GetStrings 関数を入力し、以下を使用します。
static IEnumerable<Record> FetchRecords(this Tree tree) {
return tree.Op == "OR" ? tree.Args.SelectMany(FetchRecords).Distinct() :
tree.Op == "AND" ? tree.Args.Select(FetchRecords).Aggregate((intersect, current) => intersect.Intersect(current)) :
tree.Op == "NEAR" ? FetchValsNear(tree.Args[0].Op, tree.Args[1].Op) :
FetchValsEqual(tree.Op);
}
static IEnumerable<Record> FetchValsEqual(string s) {
throw new NotImplementedException();
}
static IEnumerable<Record> FetchValsNear(string s1, string s2) {
throw new NotImplementedException();
}
static IEnumerable<Tuple<Record, double, string[]>> OrderByRank(this IEnumerable<Record> vals) {
return from val in vals
let rank = GetRank(val)
orderby rank
let strings = GetStringsIn(val)
select Tuple.Create(val, rank, strings);
}
static string[] GetStringsIn(Record val) {
throw new NotImplementedException();
}
static double GetRank(Record val) {
throw new NotImplementedException();
}
class Tree {
public string Op;
public Tree[] Args;
}
struct Record {/*your record here--use struct so Distinct and Intersect above work naturally (or use class and override Equals)*/}
このような:
foreach (var tuple in myTree.FetchRecords().AsParallel().OrderByRank().Take(30)) {
// add to datagrid or whatever
}
これにより、単純な並列化と遅延の両方が得られるため、GetStringsIn
取得したレコード (この場合は上位 30 件) に対してのみ関数が実行されます。(セレクターは、こちらの例AND
のいずれかを使用して簡略化できることに注意してください)。IntersectAll