F# - 在列表上执行非确定性分组

Pea*_*nut 1 f# grouping

我正在研究一个我知道可以用C#解决的问题.我想向我的老板证明F#能够以更简洁的方式解决.然而,我对函数式编程的理解仍然相当不成熟.

问题:

我正在处理"交易"类列表.该类的定义如下:

type Trade(brokerId : string, productId : string, marketId : string, buySideId : string, tradeDate : string, ruleId : int) = class

    member this.BrokerId = brokerId
    member this.ProductId = productId
    member this.MarketId = marketId
    member this.BuySideId = buySideId
    member this.TradeDate = tradeDate
end
Run Code Online (Sandbox Code Playgroud)

我需要能够对交易进行分组,然后将规则应用于每个结果数据组.

但是我无法保证数据的分组,即每次运行程序时确定分组的规则都可能会发生变化 - 例如,我可能需要分组:

  • TradeDate,BrokerId
  • 仅限TradeDate
  • TradeDate,BrokerId,AccountId

... 等等.

一旦我拥有了不同的组,我就很容易(我认为)应用一个规则(例如'总的TradeAmount大于10,000').

任何有关为此问题创建功能导向解决方案的帮助/指示都将非常受欢迎.

非常感谢.

Tom*_*cek 9

如果我正确理解了问题,那么你基本上想要调用该Seq.groupBy函数.问题是您在编写代码时并不完全知道要将其作为参数传递的lambda函数,因为函数可能会根据应该用于分组的键的选择而有所不同.这是一个相对简单的方法来做到这一点......

我们将创建一个函数字典,它为我们提供了一个读取指定属性的函数Trade(原则上,这可能是自动构造的,但是编写它可能更容易):

let keyfunctions : IDictionary<string, Trade -> obj> = 
  dict [ "TradeDate", (fun t -> box t.TradeDate);  
         "BrokerId", (fun t -> box t.BrokerId);
         "MarketId", (fun t -> box t.MarketId); ]
Run Code Online (Sandbox Code Playgroud)

现在,如果我们想要使用多个键,我们需要一种方法来组合两个函数,这些函数将键的一部分组合到一个函数中.我们可以编写一个组合器,它接受两个函数并返回一个生成盒装元组的单个函数:

let combine f1 f2 = (fun t -> box (f1 t, f2 t))
Run Code Online (Sandbox Code Playgroud)

如果你有一个指定键的字符串列表,那么你只需要从字典中为每个键选择函数,并使用以下方法将它们组合成一个函数combine:

let grouping = [ "TradeDate"; "MarketId" ]
let func = grouping |> Seq.map (fun n -> keyfunctions.[n]) |> Seq.reduce combine
Run Code Online (Sandbox Code Playgroud)

现在你有一个可以用作参数的函数Seq.groupBy:

trades |> Seq.groupBy func
Run Code Online (Sandbox Code Playgroud)

在F#中可能有其他方法可以做到这一点,但我认为这是一个相对简单的方法,可以说服你的老板:-).作为旁注,您可以在C#3.0中编写基本相同的内容,尽管由于语法更加繁重而看起来有点丑陋......

编辑1:这种方法的一个好处是你不需要使用任何反射.一切都以编译代码的形式运行,因此效率非常高.组合函数只调用其他几个函数(.NET方法)并将返回值装箱...

编辑2:关于顺序 - 这种方法将起作用(当比较元组时,首先比较第一个元素),但我不完全确定在使用时项目的聚合顺序Seq.reduce,所以也许这个例子在另一个方面起作用...