繁体   English   中英

如何在f#中为受歧视的联合定义运算符

[英]How to define operators for discriminated unions in f#

我有代码来实现基元之间的一些几何操作

type point = double * double
type shape =
    | Point of point
    | Line of point * point
    | Vector of point
    | Circle of point * double
    with
    member this.ToString = function
        | Point (x,y) -> sprintf "(%f; %f)" x y
        | Vector (x,y) -> sprintf "(%f; %f)" x y
        | Line ((x0,y0),(x1,y1)) -> sprintf "(%f; %f)->(%f; %f)" x0 y0 x1 y1
        | Circle ((x0,y0),radius) -> sprintf "(%f; %f)r%f" x0 y0 radius


let inline (-) (Point (x0,y0)) (Point (x1,y1)) = Vector (x0-x1,y0-y1) 
let inline (+) (Point (x0,y0)) (Vector (x1,y1)) = Point (x0+x1,y0+y1)

并且编译器说操作符上的模式匹配并不详尽,尽管这只是一个警告。 如何在没有编译器抱怨的情况下,只在DU的特定子类型之间正确实现运算符?

运算符通常定义为静态成员:

type shape =
    ...
    static member (-) (x, y) =
        match x, y with
        | Point (x0,y0), Point (x1,y1) -> Vector (x0-x1,y0-y1) 
        | Point (x0,y0), Vector (x1,y1) -> Point (x0+x1,y0+y1)
        | _ -> failwith "invalid arguments"

关于你的尝试的一些注意事项:

  1. union case不是类型,因此它们不能用于定义方法重载
  2. 函数不能重载

作为一个侧面说明,你有另外一个问题,这是ToString应符合在this ,但现在在一个匿名参数(相匹配,而不必型unit -> string ,它的shape -> string 。此外,它应该是用override声明,而不是member (这也会指出签名是错误的)。

基本问题是在编译时,编译器不知道您选择创建哪个特定形状实例。 因此,任何限制都必须在运行时完成,或者对类型施加额外的约束。 我认为运行时检查最优雅的解决方案就像是

type shape = ...
    static member (-) (a,b) = 
        match (a,b) with 
        |Point(c,d),Point(e,f) -> ... 
        |Point(c,d),Vector(e,f) -> ...
        | _ -> failwith "Can't add these shapes"

或者,您可以更改形状以将点和矢量作为不同DU的子类型,如下所示

type addable = |Point of point |Vector of point

然后相应地修改shape

我会做以下事情:

type PointT = double * double
type Shape =
    | Point of PointT
    | Line of PointT * PointT
    | Vector of PointT
    | Circle of PointT * double
    with
    member this.ToString = function
        | Point (x,y) -> sprintf "(%f; %f)" x y
        | Vector (x,y) -> sprintf "(%f; %f)" x y
        | Line ((x0,y0),(x1,y1)) -> sprintf "(%f; %f)->(%f; %f)" x0 y0 x1 y1
        | Circle ((x0,y0),radius) -> sprintf "(%f; %f)r%f" x0 y0 radius

let inline (-) (p0 : Shape) (p1 : Shape) : Shape option =
    match p0, p1 with
    | Point(x0, y0), Point(x1, y1) -> Some(Vector(x0 - x1, y0 - y1))
    | _ -> None

let inline (+) (p0 : Shape) (p1 : Shape) : Shape option =
    match p0, p1 with
    | Point(x0, y0), Vector(x1, y1) -> Some(Point(x0 + x1, y0 + y1))
    | _ -> None

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM