2010-05-11 39 views
10

Tôi muốn quá tải toán tử (/) trong F # cho chuỗi và giữ nguyên ý nghĩa của các số.Toán tử quá tải trong F #: (/)

/// Combines to path strings 
let (/) path1 path2 = Path.Combine(path1,path2) 

let x = 3/4 // doesn't compile 

Nếu tôi thử những điều sau đây tôi nhận được "Cảnh báo 29 Thành viên mở rộng không thể cung cấp quá tải nhà điều hành. Hãy cân nhắc định nghĩa toán tử như một phần của định nghĩa loại."

/// Combines to path strings 
type System.String with 
    static member (/) (path1,path2) = Path.Combine(path1,path2) 

Bất kỳ ý tưởng nào?

Kính trọng, forki

+0

Các bạn đã cố gắng định nghĩa nó như op_Division? CẬP NHẬT: đừng bận tâm; nó sẽ không hoạt động – pblasucci

+0

Tôi không nghĩ rằng bạn có thể quá tải thành viên (hoặc nhà khai thác) trên các lớp học từ trước. – Gabe

Trả lời

18

Bạn không thể cung cấp các nhà khai thác quá tải với nhiều loại hiện có. Một tùy chọn là sử dụng tên toán tử khác (như Natahan gợi ý). Tuy nhiên, bạn cũng có thể định nghĩa một kiểu mới để đại diện cho đường dẫn trong # mã F của bạn và cung cấp cho các nhà điều hành / cho loại hình này:

open System  

// Simple type for representing paths 
type Path(p) = 
    // Returns the path as a string 
    member x.Path = p 
    // Combines two paths 
    static member (/)(p1:Path, p2:Path) = 
    Path(IO.Path.Combine(p1.Path, p2.Path)) 

let n = 4/2 
let p = Path("C:\\")/Path("Temp") 

này có một lợi ích quan trọng - bằng cách làm cho các loại rõ ràng hơn, bạn cung cấp cho kiểm tra loại thêm thông tin mà nó có thể sử dụng để xác minh mã của bạn. Nếu bạn sử dụng chuỗi để biểu thị đường dẫn, thì bạn có thể dễ dàng nhầm lẫn đường dẫn với một số chuỗi khác (ví dụ: tên). Nếu bạn xác định loại Path của mình, trình kiểm tra loại sẽ ngăn bạn mắc lỗi này.

Hơn nữa, trình biên dịch sẽ không cho phép bạn (đơn giản) kết hợp đường dẫn không chính xác (mà có thể dễ dàng xảy ra nếu bạn đại diện cho những con đường như dây đàn), vì p + p không được định nghĩa (bạn có thể chỉ sử dụng /, mà chính xác sử dụng Path.Combine) .

+2

Thật tuyệt khi một người viết một cuốn sách về chủ đề trả lời một câu hỏi! –

+1

Hrm, về cú pháp dọc theo các dòng sau: (let p = Path "C: \\"/"Temp") bằng cách cung cấp p1: Path, P2: string overload? –

5

Tôi không nghĩ rằng đây là có thể trong F #, dựa trên một đọc the overloading documentation.

Thay vào đó, tôi khuyên bạn nên tạo chức năng của riêng mình mà trông giống như/ nhưng không. Một cái gì đó như:

let (</>) path1 path2 = Path.Combine (path1,path2) 

này có khả năng là ít gây phiền nhiễu trong thời gian dài vì nó không gây rối với những suy luận kiểu ngầm rằng người đọc của con người là running-- / có nghĩa là kết quả là một dấu chấm động, và nhớ rằng đôi khi một chuỗi là gánh nặng *. Nhưng sau lần đầu tiên người đọc nhìn thấy </>, thật dễ dàng để nhớ rằng nó có liên quan đến biểu tượng được nhúng ở giữa.

* Tôi nghĩ lý do duy nhất + cho chuỗi có vẻ OK là quá phơi sáng. Sau khi sử dụng Haskell hoặc Caml trong một thời gian dài, vài phút đầu tiên sau khi chuyển sang ngôn ngữ khác sẽ làm cho số điện thoại của bạn trở nên tồi tệ.

+0

Tôi đang sử dụng (@@) vào lúc này, nhưng tôi nghĩ (/) sẽ đẹp hơn. – forki23

8

Tôi không nghĩ rằng có một cách đơn giản để làm điều đó. Các thành viên mở rộng không được xem xét cho quá tải nhà điều hành trong F #, và không có cách nào tốt để xác định lại hoạt động theo cách bán chung bằng cách sử dụng các ràng buộc thành viên.

Có thể hack một cái gì đó với nhau rằng sẽ làm việc, nhưng nó rất xấu xí:

type DivisionOperations = 
    static member Divide(x:int, y:int) = x/y 
    static member Divide(path1, path2) = Path.Combine(path1, path2) 

let inline div< ^t, ^a, ^b, ^c when (^t or ^a) : (static member Divide : ^a * ^b -> ^c)> a b = ((^t or ^a) : (static member Divide : ^a * ^b -> ^c) (a, b)) 

let inline (/) x y = div<DivisionOperations, _, _, _> x y 
8

Thực ra bạn có thể.

Hãy thử điều này:

open System.IO 

type DivExtension = DivExtension with 
    static member inline (=>) (x    , DivExtension) = fun y -> x/y 
    static member  (=>) (x    , DivExtension) = fun y -> Path.Combine(x, y) 
    static member  (=>) (x:DivExtension, DivExtension) = fun DivExtension -> x 

let inline (/) x y = (x => DivExtension) y 
+2

Lưu ý rằng điều này thực sự khá giống với câu trả lời của tôi, mặc dù bằng cách thêm phần tử giả «DivExtension * DivExtesion -> DivExtension -> DivExtension', bạn có thể sử dụng tổng quát quá tải đầu tiên trong khi ngăn trình biên dịch mặc định' (/) 'nhà điều hành đến quá tải thứ hai, rất thông minh. Tôi cảm thấy rằng việc bạn sử dụng toán tử ký hiệu '(=>)' làm cho phần thân của '(/)' khó hiểu hơn, mặc dù bạn có thể bỏ qua các ràng buộc thành viên tĩnh rõ ràng. – kvb

+0

Vâng, bạn nói đúng. Tôi thích sử dụng các toán tử trung gian để tránh viết các ràng buộc tĩnh. Trong câu trả lời của bạn, bạn cũng có thể thêm quá tải giả và nó sẽ tổng quát là tốt. Một điều tôi không thể làm mà không có nhà khai thác là viết hai lần 'hoặc', tôi có nghĩa là một cái gì đó như khi (^ t hoặc^a hoặc^b). Trong những trường hợp đó tôi sử dụng toán tử bậc ba, tôi nghĩ đó là cách duy nhất. – Gustavo

+0

Xin chào, tôi đã thử nghiệm và nó hoạt động, nhưng tôi thực sự không hiểu tại sao (tôi mới đến F #) ... – Liviu