簡體   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