7

以下で私がやろうとしていることを達成する別の方法がある場合は、私に知らせてください。次のサンプルコードがあるとします

type FooBar = 
  | Foo
  | Bar

let foobars = [Bar;Foo;Bar]

let isFoo item  = 
  match item with
  | Foo _ -> true
  | _ -> false

foobars |> Seq.filter isFoo

isFooの汎用/高次バージョンを作成して、他のすべてのタイプの識別された共用体(この場合はBar)に基づいてリストをフィルター処理できるようにします。

次のようなものです。ここで、'aはFooまたはBarのいずれかです。

let is<'a> item  = 
  match item with
  | a _ -> true
  | _ -> false

ただし、この試行では次のエラーが発生します。

エラーFS0039:パターン弁別器「a」が定義されていません

4

2 に答える 2

5

リストをフィルタリングするだけの場合、最も簡単なオプションは、function標準のパターンマッチングを作成するために使用することです。

[ Foo; Bar; Foo ]
|> List.filter (function Foo -> true | _ -> false)

ケースをチェックしてから何か他のことを行う、より複雑なジェネリック関数を作成したい場合、最も簡単なオプション(一般的に機能します)は、trueまたはfalse:を返す述語を取ることです。

let is cond item  = 
  if cond item then
    true
  else
    false

// You can create a predicate using `function` syntax
is (function Foo -> true | _ -> false) <argument>

特定の例では、どのケースにもパラメーターがない、識別された共用体があります。これはおそらく非現実的な単純化ですが、パラメーターのない識別された共用体のみを気にする場合は、ケースを値として使用して比較することができます。

let is case item = 
  if case = item then
    true
  else
    false

// You can just pass it 'Foo' as the first parameter to 
// `is` and use partial function application
[ Foo; Bar; Foo ]
|> List.filter (is Foo)

// In fact, you can use the built-in equality test operator
[ Foo; Bar; Foo ] |> List.filter ((=) Foo)

この最後の方法は、場合によってはパラメーターを持つ、より複雑な識別された共用体がある場合は機能しないため、おそらくあまり役​​に立ちません。たとえば、オプション値のリストがある場合:

let opts = [ Some(42); None; Some(32) ]
opts |> List.filter (is Some) // ERROR - because here you give 'is' a constructor 
                              // 'Some' instead of a value that can be compared. 

Reflectionを使用して(指定された名前のケースをチェックするために)さまざまなトリックを実行できます。また、F#引用符を使用して、より適切で安全な構文を取得することもできますが、を使用してパターンマッチングを使用すると、functionかなりの価値があるとは思いません。明確なコード。

編集-好奇心から、リフレクションを使用するソリューション(そして、低速で、タイプセーフではなく、実際に何をしているのかを本当に理解していない限り、実際に使用するべきではありません)は次のようになります。

open Microsoft.FSharp.Reflection
open Microsoft.FSharp.Quotations

let is (q:Expr) value = 
  match q with
  | Patterns.Lambda(_, Patterns.NewUnionCase(case, _)) 
  | Patterns.NewUnionCase(case, _) ->
      let actualCase, _ = FSharpValue.GetUnionFields(value, value.GetType())
      actualCase = case
  | _ -> failwith "Wrong argument"

引用符を使用してユニオンケースを識別するため、次のように記述できます。

type Case = Foo of int | Bar of string | Zoo

[ Foo 42; Zoo; Bar "hi"; Foo 32; Zoo ]
|> List.filter (is <@ Foo @>)
于 2012-10-25T14:15:01.900 に答える
3

ユニオンケースが同じパラメーターのセットを受け入れる限り、コンストラクターを引数として渡し、比較のためにDUを再構築できます。

Foo次の場合に、より魅力的に見え、Barパラメータがあります。

type FooBar = Foo of int | Bar of int

let is constr item = 
    match item with
    | Foo x when item = constr x -> true
    | Bar x when item = constr x -> true
    | _ -> false

あなたの例では、コンストラクターには引数がありません。isしたがって、より簡単な方法で書くことができます。

type FooBar = Foo | Bar

let is constr item = item = constr

[Bar; Foo; Bar] |> Seq.filter (is Foo)
于 2012-10-25T14:18:42.737 に答える