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

时间:2016-09-10 21:03:39

标签: f#

假设我有这样的DU:

type DU = Number of int | Word of string

假设我创建了一个列表:

[Number(1); Word("abc"); Number(2)]

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

2 个答案:

答案 0 :(得分:5)

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

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

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

 allTheSameCase (function Number _ -> 0 | Word _ -> 1) lst

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

 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.

答案 1 :(得分:4)

如果您想检查列表的元素是否属于特定的并集案例,则可以直接提供谓词函数。

let isNumbers = List.forall (function Number _ -> true | _ -> false)

如果你不关心哪个联合案例,只要它们都是一样的,你需要明确拼写它们。除非反射魔法获得未在F#内部暴露的属性,否则还需要为每个案例分配一些值。为了避免不得不考虑任意值,我们可以使用一个活动模式,在幕后映射到不同的DU。

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

let isSameCase src = 
    src |> Seq.groupBy (|IsNumber|IsWord|) |> Seq.length <= 1