.net – F#:成对减少/聚合序列或列表

我对函数式编程相当新,并且在列表处理任务方面存在一些问题.我有一系列记录,如下所示:

type TestRec = {
    Id : string
    Amount : int }

现在我想删除列表中彼此构建一对的所有项目.例如,如果有两个项目的金额为7和-7,则应从列表中删除这两个项目.如果存在Amount = 7的第三个元素,则它应该保留在列表中.

我希望你们能理解我想要做的事情.这是我到目前为止提出的(但它还没有正常工作):

let removeDoubles items =
    items
    |> Seq.groupBy (fun i -> Math.Abs(i.Amount))
    |> Seq.map snd
    |> Seq.filter (fun i -> Seq.length i = 1)

编辑:
确定两个元素是否彼此匹配的函数可能比上面描述的更复杂(Math.Abs​​).我认为这将是Amount值的一个很好的例子,但它可以是任何谓词函数.

编辑2:
为了澄清更多,我想对可能的相关问题给出更真实的描述.您可以想象一下发票的计算,其中列表包含所有发票头寸.现在,您要删除所有具有相同“商品编号”,“货币”且价格评估为零的发票头寸对.

也许这个例子有助于解释我的问题.我只是认为解决这个问题可能有一个更“功能性的方法”,就是让两个循环在列表上运行并删除像我在命令式语言中那样的元素.

解决方法

为了抽象出对立的想法,我定义了两个函数.一个用于关系相等(相关),另一个用于定义取消(相反).

该功能首先将相关对象分组在一起,然后将它们分成相对的数组.然后根据所需的取消数量对这些得到的阵列进行切片.最后,所有内容都汇总在一起.

type TestRec = {
    Id : string;
    Amount : int;
}

let removeDoubles items related opposite =
    items
    |> Seq.groupBy related
    |> Seq.map (fun (key,values) -> 
        let t,f = values |> Seq.toArray |> Array.partition opposite
        if t.Length > f.Length then
            t.[.. t.Length - f.Length - 1]
        else
            f.[.. f.Length - t.Length - 1]
        )
    |> Seq.concat

let items = [
    {Id="first";Amount=7}; 
    {Id="seconds";Amount=7};
    {Id="we";Amount=4}; 
    {Id="negative";Amount= -7}
    ]

let test = removeDoubles 
               items 
               (fun x -> abs x.Amount) 
               (fun x -> x.Amount > 0)

printf "%A" test

System.Console.ReadLine() |> ignore

输出

seq [{Id = "first";
      Amount = 7;}; {Id = "we";
                     Amount = 4;}]

相关文章

什么是设计模式一套被反复使用、多数人知晓的、经过分类编目...
单一职责原则定义(Single Responsibility Principle,SRP)...
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强...
适配器模式将一个类的接口转换成客户期望的另一个接口,使得...
策略模式定义了一系列算法族,并封装在类中,它们之间可以互...
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,...