如何判断受歧视的联合类型列表是否属于同一情况?

Dav*_* Ng 5 f#

假设我有一个像这样的DU:

type DU = Number of int | Word of string
Run Code Online (Sandbox Code Playgroud)

假设我创建了一个列表:

[Number(1); Word("abc"); Number(2)]
Run Code Online (Sandbox Code Playgroud)

如何编写一个函数,对于所有元素都相同的DU列表返回true.对于上面的列表,它应该返回false.

kae*_*fer 6

如果您想检查列表的元素是否属于特定联合情况,提供谓词函数很简单。

let isNumbers = List.forall (function Number _ -> true | _ -> false)
Run Code Online (Sandbox Code Playgroud)

如果您不在乎哪个联合案例,只要它们都相同,您就需要明确地将它们全部拼写出来。除非使用反射魔法来获取未在 F# 中公开的属性,否则您还需要为每种情况分配一些值。为了避免必须考虑任意值,我们可以采用一种活动模式,该模式在幕后映射到不同的 DU。

let (|IsNumber|IsWord|) = function
| Number _ -> IsNumber
| Word _ -> IsWord

let isSameCase src = 
    src |> Seq.groupBy (|IsNumber|IsWord|) |> Seq.length <= 1
Run Code Online (Sandbox Code Playgroud)


scr*_*wtp 5

我在这里使用的一般方法是将union值映射到标识案例的标签中,然后检查生成的标签集是否最多只有一个元素.

let allTheSameCase (tagger: 'a -> int) (coll: #seq<'a>) = 
    let cases = 
        coll
        |> Seq.map tagger
        |> Set.ofSeq
    Set.count cases <= 1 
Run Code Online (Sandbox Code Playgroud)

对于标记器功能,您可以手动分配标签:

 allTheSameCase (function Number _ -> 0 | Word _ -> 1) lst
Run Code Online (Sandbox Code Playgroud)

或使用反射(请注意,您可能需要根据需要设置绑定标志):

 open Microsoft.FSharp.Reflection

 let reflectionTagger (case: obj) = 
    let typ = case.GetType()
    if FSharpType.IsUnion(typ)
        then 
            let info, _ = FSharpValue.GetUnionFields(case, typ) 
            info.Tag
        else -1 // or fail, depending what makes sense in the context.
Run Code Online (Sandbox Code Playgroud)