在F#中适当使用主动模式

3

我正在使用一个活动模式来解析csv格式的用量日志中的使用事件。以下是活动模式部分。整个文件的解析效果很好,产生的序列充满了各种使用事件。

type SystemName = string
type SystemVersion = string
type MAC = string
type Category = string
type Game = string
type Setting = string
type StartupLocation = string

type UsageEvent =
    | SystemStart of DateTime * SystemVersion * SystemName * MAC
    | SystemEnd of DateTime
    | GameStart of DateTime * Category * Game * Setting * StartupLocation
    | GameEnd of DateTime * Category * Game
    | Other

let (|SystemStart|SystemEnd|GameStart|GameEnd|Other|) (input : string list) =
    match List.nth input 0 with
    | "SystemStartedEvent" ->
         SystemStart (DateTime.Parse (List.nth input 1), List.nth input 2, List.nth input 3, List.nth input 4)
    | "SystemEndedEvent" ->
         SystemEnd (DateTime.Parse (List.nth input 1))
    | "GameStartedEvent" ->
         GameStart (DateTime.Parse (List.nth input 1), List.nth input 2, List.nth input 3, List.nth input 4, List.nth input 5)
    | "GameEndedEvent" ->
         GameEnd (DateTime.Parse (List.nth input 1), List.nth input 2, List.nth input 3)
    | _ ->
         Other

我的问题在于我可能使用了错误的ActivePattern。我想要遍历列表并根据一些逻辑创建一个树形结构,但是在解析后没有办法匹配序列中的条目。

let CountSystemStart (entries : UsageEvent list) =
    let rec loop sum = function
        | SystemStart(_,_,_,_) -> sum + 1
        | _ -> sum
    loop 0 entries

这种匹配不起作用,因为循环函数需要一个字符串列表。我还能以其他方式使用联合中包含的数据吗?或者我应该先匹配输入,然后将其存储在常规类型中?

2个回答

7
补充一下@Petr的答案 - UsageEvent和您的活动模式案例具有相同的名称,因此定义较晚的活动模式会遮盖联合类型。很可能是这样产生了string list。我建议直接放弃活动模式,并向UsageEvent添加一个Parse函数(或者更准确地说是一个ParseParts函数,因为您希望将其提供给字符串列表)。
type UsageEvent =
    | SystemStart of DateTime * SystemVersion * SystemName * MAC
    | (...and so on...)
    static member ParseParts (input: string list) =
        match input with
        | ["SystemStartedEvent"; date; version; name; mac] ->
            SystemStart (DateTime.Parse date, version, name, mac)
        | (...and so on...) 

活动模式很可爱,但您需要一个好的场景来展现它们的优势。否则,如果您可以使用普通函数,就请使用普通函数。


2

这段代码有两个问题:

  1. 区分联合类型 UsageEvent 和活动模式选择函数具有相同的名称

  2. 递归循环函数并不是递归的,因为它没有调用自己。

当您匹配 UsageEvent 列表时,请尝试使用完整的类型名称。

我会将您的 CountSystemStart 函数重写为:

let CountSystemStart (entries : UsageEvent list) =
    let rec loop sum = function
        | [] -> sum 
        | (UsageEvent.SystemStart(_))::rest -> loop (sum + 1) rest
        | _::rest -> loop sum rest
    loop 0 entries  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接