5

私はF#を初めて使用し、パターンマッチングのアイデア全体に精通していません。問題のより良い解決策を探しましたが、問題を適切に表現することすらできないのではないかと心配しています。質問のタイトルが少なくともある程度正確であることを願っています。

私がやりたいのは、から2つの「パラメータ」を抽出することですlistMethodlistMethod文字列と「パラメータ」を持ついくつかのタイプの1つですExpression(パラメータは間違った用語だと思います):

    let (varDecl, listExpr) =
        match listMethod with 
        | Select (var, expr)  -> (var, expr)
        | Where (var, expr)   -> (var, expr)
        | Sum (var, expr)     -> (var, expr)
        | Concat (var, expr)  -> (var, expr)

次に、作業を続けvarDecl、最後に、に基づいて作成したいくつかの一時変数を使用する実際のlistMethodコードと同様の一致式を作成しvarDeclます。

私の質問は次のとおりです。上記のコードをよりコンパクトにするにはどうすればよいですか?

string2つのパラメーター(typeとExpression)を持つすべてのタイプを自分でリストせずに一致させたいのですが、これはちょっと醜くて保守が困難です。

タイプは次のListMethodように宣言されます(すべてがFsLex / FsYaccプロジェクトです)。

type ListMethod =
    | Select of string * Expr
    | Where of string * Expr
    | Sum of string * Expr
    | Concat of string * Expr
    | ...
    | somethingElse of Expr

(現在、私はフォームのタイプしか持っていませんstring * Exprが、それは変更されます)。

これは、経験のある人にとってはかなり馬鹿げた質問だと思いますが、私が言ったように、私はF#を初めて使用し、自分で解決策を見つけることができませんでした。

前もって感謝します!

編集: 私は本当にすべての可能なタイプをlistMethod2回リストすることを避けたいです。式でワイルドカードまたはプレースホルダーを使用する方法がない場合matchは、タイプを変更して、listMethodよりクリーンにすることができます。

頭に浮かぶオプションの1つは、1つのタイプのみlistMethodを作成し、具象タイプの3番目のパラメーター(Select、Where、Sum)を作成することです。それとももっと良いアプローチがありますか?

4

4 に答える 4

9

これはおそらく標準的な方法です。

let (varDecl, listExpr) =
    match listMethod with 
    | Select (var, expr)
    | Where (var, expr)
    | Sum (var, expr)
    | Concat (var, expr) -> (var, expr)

|記号はを意味するため、これらのorいずれかが一致すると、結果が返されます。すべてのケースがまったく同じ名前(およびタイプ)であることを確認してください。

Chuckがコメントしたように、これはさらに優れたソリューションです。

let (Select (varDecl, expr)
    | Where (varDecl, expr)
    | Sum (varDecl, expr)
    | Concat (varDecl, expr)) = listMethod
于 2011-08-30T08:00:06.473 に答える
6

これは、経験のある人にとってはかなり馬鹿げた質問だと思いますが、私が言ったように、私はF#を初めて使用し、自分で解決策を見つけることができませんでした。

それどころか、これは非常に良い質問であり、F#はこの点で他の言語とは異なるため、実際には比較的未踏の根拠です(たとえば、OCamlのポリモーフィックバリアントを使用してこの問題を解決できる場合があります)。

Ankurが書いたように、最善の解決策は常にデータ構造を変更して、可能であれば必要なことを簡単に実行できるようにすることです。アクティブパターンを使用するKVBのソリューションは、他の言語ではその言語機能が一般的ではないため、価値があるだけでなく斬新でもあります。or-patternsを使用してマッチケースを組み合わせるというRamonの提案も良いですが、不完全なパターンマッチを書きたくありません。

おそらく、実際に発生するこの問題の最も一般的な例は、演算子です。

type expr =
  | Add of expr * expr
  | Sub of expr * expr
  | Mul of expr * expr
  | Div of expr * expr
  | Pow of expr * expr
  | ...

次のようにタイプを再構築できます。

type binOp = Add | Sub | Mul | Div | Pow

type expr =
  | BinOp of binOp * expr * expr
  | ...

次に、部分式の抽出などのタスク:

let subExprs = function
  | Add(f, g)
  | Sub(f, g)
  | Mul(f, g)
  | Div(f, g)
  | Pow(f, g) -> [f; g]
  | ...

より簡単に実行できます:

let subExprs = function
  | BinOp(_, f, g) -> [f; g]
  | ...

最後に、共有インターフェイスの実装などのOOP構造体を使用して、F#型(共用体型など)を拡張できることを忘れないでください。これは、共通性を表現するためにも使用できます。たとえば、2つのタイプに2つの重複する要件がある場合、この共通性を公開するために、両方に同じインターフェイスを実装させることができます。

于 2011-08-30T14:49:45.730 に答える
4

データ構造を調整しても問題がない場合は、以下にパターンマッチングを容易にするものを示します。

type ListOperations = 
    Select | Where | Sum | Concat


type ListMethod =
    | ListOp of ListOperations * string * Expr
    | SomethingElse of int

let test t = 
    match t with
    | ListOp (a,b,c) -> (b,c)
    | _ -> ....

データ構造は、実行する操作を念頭に置いて設計する必要があります。

于 2011-08-30T10:57:00.693 に答える
3

Selectすべてのケースを同じように処理したい場合とWhere、、、、などを処理しているかどうかに基づいて異なる方法で処理したい場合がある場合Sum、1つの解決策はアクティブパターンを使用することです。 :

let (|OperatorExpression|_|) = function
| Select(var, expr) -> Some(Select, var, expr)
| Where (var, expr) -> Some(Where, var, expr)
| Sum (var, expr) -> Some(Sum, var, expr)
| Concat (var, expr) -> Some(Concat, var, expr)
| _ -> None

これで、ケースを個別に処理する必要がある場合でも通常どおりに一致させることができますが、アクティブなパターンを使用して一致させることもできます。

let varDecl, listExp = 
    match listMethod with
    | OperatorExpression(_, v, e) -> v, e
    | _ -> // whatever you do for other cases...
于 2011-08-30T13:02:00.133 に答える