3

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

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 的特定子类型之间正确实现运算符?

4

4 回答 4

4

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

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. 函数不能重载
于 2013-06-11T21:44:55.073 回答
3

附带说明一下,您还有另一个问题,那就是ToString应该匹配 on this,但现在匹配匿名参数(而不是 type unit -> string,它是shape -> string。此外,它应该声明为,而override不是member出签名是错误的)。

于 2013-06-11T21:46:23.027 回答
1

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

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

于 2013-06-11T21:46:58.330 回答
1

我会做以下事情:

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
于 2013-06-11T21:47:19.230 回答