2013-06-11 41 views
3

我要實現原語如何定義識別聯合運營商在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的特定子類型之間正確實現運算符?

回答

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. 工會的情況下都沒有的類型,所以它們不能被用來定義方法重載
  2. 功能不能超載
+0

我終於明白,沒有類型安全的設計和DU設計之間略有緊張。在編譯時,我想知道我不能從一個圓中減去一個矢量,而只能從一個點中減去一個矢量。然而在其他時候,我可能希望在不同類型的形狀上進行模式匹配,並讓編譯器確保模式匹配儘可能詳盡,例如DU的可能性。我想我會選擇一個面向對象的層次結構,因爲它適合我需要的更好的東西。我想我有很多DU的使用形狀介紹教程(即http://msdn.microsoft.com/en-us/library/dd233226.aspx) – bradgonesurfing

+0

旁邊跟蹤雖然這個答案可能會給我我需要的東西http ://stackoverflow.com/a/8133470/158285 – bradgonesurfing

3

作爲一個方面說明,你有另一個問題,即ToString應匹配this,但現在匹配匿名參數(而不是類型unit -> string,它是shape -> string。此外,應該用override而不是member(它也指出簽名是錯誤的)。

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

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