You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

F#如何实现可区分联合列表按分支类型去重

可区分联合类型按分支去重实现方法

问题说明

需求为构造可区分联合类型的列表,约束为同一联合分支仅允许存在一个元素,去重判定与分支关联的底层数据无关。
示例代码:

type Car = 
| Honda 
| Tesla of string 

let carList = [Honda; Tesla("Tesla"); Tesla(""); Tesla("Tesla")]

// 预期去重结果
let correctCarList = [Honda; Tesla ("Tesla")]

该规则下,只要元素属于Tesla分支,无论携带的字符串内容是什么,都判定为重复项,列表仅保留第一个出现的该分支元素即可。
默认Set结构无法满足需求:Set判定重复的逻辑是分支类型+关联底层数据完全相等,上述示例用Set去重只会移除完全重复的Tesla("Tesla"),会同时保留Tesla("Tesla")和Tesla("")两个同分支元素,不符合要求。

可行实现方案

完全可以实现仅按可区分联合分支类型的去重逻辑,没有语法层面的限制,常见实现方式有两种:

方案1:反射提取分支标签(通用方案)

通过FSharp.Reflection模块提供的API,可以直接获取任意可区分联合实例对应的分支标签,不需要为每个类型单独写匹配逻辑,适配所有可区分联合类型:

open FSharp.Reflection

let distinctByUnionCase (list: 'T list) =
    if not (FSharpType.IsUnion(typeof<'T>)) then
        invalidArg "list" "入参必须为可区分联合类型列表"
    list
    |> List.indexed
    |> List.map (fun (idx, item) ->
        let unionCase = FSharpValue.GetUnionFields(item, typeof<'T>) |> fst
        (unionCase.Name, idx, item))
    |> List.distinctBy (fun (caseName, _, _) -> caseName)
    |> List.sortBy (fun (_, idx, _) -> idx)
    |> List.map (fun (_, _, item) -> item)

// 测试验证
let carList = [Honda; Tesla("Tesla"); Tesla(""); Tesla("Tesla")]
let correctCarList = distinctByUnionCase carList
// 运行结果为 [Honda; Tesla "Tesla"],符合预期

方案2:手动映射分支标识(高性能方案)

如果不想引入反射依赖,或者对运行性能有更高要求,可以手动编写匹配逻辑,将每个分支映射为独立的标识值做去重键,适合分支数量固定且较少的场景:

// 为Car类型编写分支标识映射
let getCarCaseKey = function
    | Honda -> 0
    | Tesla _ -> 1

let correctCarList = carList |> List.distinctBy getCarCaseKey

两种方案都可以实现「同分支仅保留一个元素、忽略分支携带数据差异」的约束。


内容的提问来源于stack exchange,提问作者JavaHava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:39:17