1

我对 F# 和活动模式还很陌生,我遇到了一个我无法解释的异常情况。

module Eval =
let (|Bet|Pass|) (test:BetChoice) =
    match test with
        | BetChoice.Bet -> Bet
        | BetChoice.Pass -> Pass

let (|NoBet|Bet|Pass|) (test:Nullable<BetChoice>) : Choice<unit, unit, unit> =
    match test.HasValue with
        | true -> match test.Value with 
                      | BetChoice.Bet -> Bet
                      | BetChoice.Pass -> Pass
        | false -> NoBet

let FlipByWinner ((value:int), (awins:bool)) =
    match awins with
    | true -> (value, -value)
    | false -> (-value, value)

let Evaluation (awins:bool) (player11:BetChoice) (player21:BetChoice) (player12:Nullable<BetChoice>) =
     match player11 with
     | Pass -> match player21 with
               | Pass -> FlipByWinner(1, awins)
               | Bet-> match player12 with
                       | Bet -> FlipByWinner(2, awins)
                       | Pass -> FlipByWinner(1, false)
                       | NoBet -> raise (System.ArgumentException("invalid strategy"))
     | Bet ->  match player21 with
               | Bet -> FlipByWinner (2, awins)
               | Pass -> FlipByWinner (1, false)

这不编译。通过一个小的调整,我可以让它按预期工作,但我不知道到底发生了什么让我有点紧张......第二种模式可以重命名为“(|NoBet|Bet1|Pass1| )" 并且它的关联模式在整个代码中都发生了变化,然后它就可以工作了,但我真的不明白为什么会有类型不匹配异常。

还有一种处理几乎相同但不完全相同的 2 个活动模式的好方法吗?似乎应该有一种方法可以将常见的东西放在一起。(作为旁注,看起来缩进在复制/粘贴中搞砸了,这完全是模块 Eval 的一部分)。

4

1 回答 1

3

是的,第一个问题很有意义。在同一个命名空间中不能有两个同名的活动模式标签。这与 F# 编译器实际生成活动模式名称的方式有关。如果您查看反射器,则会生成以下代码:

type Alpha = Foo | Bar
let (|Foo|Bar|) = ...

是非常不同的,尽管在概念上主动模式和区分联合是非常相似的概念。

无论如何,您要解决的问题是,如果您有两组大部分相似/但不同的活动模式,该怎么办。我会敦促你做的是使用部分活动模式。请参阅此博客文章

在示例中,您似乎想要以下内容:

let (|IsBet|_|) = ...
let (|IsPass|_|) = ...

这样您就可以同时对玩家 11 和玩家 21 进行模式匹配,例如:

match player11, player21 with
| IsPass & IsPass -> ...
| IsPass & IsBet  -> ...
| IsBet  & IsPass -> ...
| IsBet  & IsBet  -> ...

这应该对清理代码大有帮助。

于 2010-09-08T22:50:09.510 回答