在设计联合类型层次结构时,似乎存在大量逻辑重复的可能性。对于一个简单而有点人为的例子,请考虑以下类型代表“灵活”数字:
type Number =
|Integer of int
|Real of float
static member (+) (x : Number, y:Number) =
match x,y with
|Integer(x), Integer(y) -> Integer(x + y)
|Integer(x), Real(y) -> Real(float x + y)
|Real(x), Integer(y) -> Real(x + float y)
|Real(x), Real(y) -> Real(x + y)
static member (-) (x : Number, y:Number) =
match x,y with
|Integer(x), Integer(y) -> Integer(x - y)
|Integer(x), Real(y) -> Real(float x - y)
|Real(x), Integer(y) -> Real(x - float y)
|Real(x), Real(y) -> Real(x - y)
其他运算符将包含类似的重复匹配逻辑。是否有一种模式/实践可用于合并一个功能中的逻辑以消除这种冗余?
(注意:我意识到在F#中有更好的处理泛型算法的方法;这个例子应该被视为一般的说明“如何在模式匹配的上下文中停止重复自己?”问题)
答案 0 :(得分:1)
您可以通过编写一个以整数和浮点算术运算符作为参数的函数来抽象(+)
和(-)
:
static member binOp (intOp : int -> int -> int) (floatOp : float -> float -> float) (x : Number, y:Number) =
match x,y with
|Integer(x), Integer(y) -> Integer(intOp x y)
|Integer(x), Real(y) -> Real(floatOp (float x) y)
|Real(x), Integer(y) -> Real(floatOp x (float y))
|Real(x), Real(y) -> Real(floatOp x y)
static member (+) (x,y) = Number.binOp (+) (+) (x,y)
static member (-) (x,y) = Number.binOp (-) (-) (x,y)
同样的功能也适用于(*)
。但是当你到达(/)
时,模式开始崩溃,因为你可能想要一个浮点结果,即使是整数输入。
根据我的经验,只需要复制就可以更简单 - 在实践中通常并没有那么糟糕,并且试图抽象太多会导致无法读取的代码。
在某些情况下,单个模式匹配案例变得相当复杂,active patterns可以帮助您抽象一次模式。我不认为它们在你的具体例子中有意义,但值得牢记。