2010-07-07 36 views
6

Tôi muốn viết một cái gì đó như thế này:trường hợp chung trong F # phân biệt đối xử đoàn

type NumExp = Num of float 

type Exp = 
    | Num of float 
    | Dot of NumExp * NumExp 
    | Op of string * Exp * Exp 

let getValue (Num(n) : NumExp) = n 

Trình biên dịch phàn nàn về một cuộc xung đột giữa NumExpExp trong getValue. Thậm chí sau thất bại:

let getValue (nn : NumExp) = match nn with | Num(n) -> n 

Có cách nào để sử dụng các trường hợp tương tự trong cả hai công đoàn phân biệt đối xử làm việc với chức năng? Bản thân định nghĩa DU là OK.

Tôi muốn sử dụng các trường hợp tương tự để tránh thêm một mức gián tiếp như

type Exp = 
    | NumExpExp of NumExp 
    | Dot of NumExp * NumExp 
    | Op of string * Exp * Exp 

trong định nghĩa Exp. Tôi cảm thấy tôi đang thiếu một cái gì đó rất cơ bản ở đây.

Lý do tôi có NumExp là tôi muốn để có thể 'cắm' 2 Exp s vào một Dot (chứ không phải 2 phao) bởi vì nó làm cho việc tạo ra biểu thức dễ dàng hơn, nhưng họ không thể có bất kỳ Exp, chỉ số .

EDIT: những gì tôi thực sự muốn biết là liệu hai trường hợp trong hai dus có thể được coi như cùng một thực thể (loại giống như Exp "bao gồm" NumExp). Tôi nhận ra bây giờ Exp.NumNumExp.Num là các thực thể hoàn toàn riêng biệt. Tomas cung cấp một cách phân biệt rõ ràng hai trường hợp dưới đây.

Trả lời

13

Nếu bạn có hai đoàn bị phân biệt với tên mâu thuẫn của các trường hợp, bạn có thể sử dụng tên đầy đủ của vụ án đoàn phân biệt đối xử:

let getValue (NumExp.Num(n)) = n 

Một ví dụ hoàn chỉnh hơn sẽ trông như thế này:

let rec eval = function 
    | Exp.Num(f) -> f 
    | Exp.Dot(NumExp.Num(f1), NumExp.Num(f2)) -> 
     // whatever 'dot' represents 
    | Exp.Op(op, e1, e2) -> 
     // operator 

Điều này luôn sử dụng tên đầy đủ, điều này có thể là một ý tưởng hay nếu tên đủ đơn giản và có những trường hợp xung đột (có thể dẫn đến nhầm lẫn).

EDIT: Về việc chia sẻ các trường hợp - không có cách nào tự động làm điều đó, nhưng bạn có thể có một trường hợp trong Exp của bạn mà chỉ đơn giản bao gồm giá trị của NumExp. Ví dụ như thế này:

type NumExp = 
    | Num of float 

type Exp = 
    // first occurrence of NumExp is just a name, but F# allows us to reuse 
    // the name of the type, so we do that (you could use other name) 
    | NumExp of NumExp 
    // other cases 

Khi viết eval chức năng sau đó bạn sẽ viết (chú ý rằng chúng tôi không còn có vấn đề với cuộc đụng độ tên, vì vậy chúng ta không cần tên đầy đủ):

| NumExp(Num f) -> f 
| Op(op, e1, e2) -> // ... 
+0

Cảm ơn Tomas, công trình định hướng kỳ diệu. Những gì tôi đã yêu cầu, mặc dù là giống như 'có thể hai trường hợp trong hai DU được coi là cùng một thực thể?', Hoặc nói cách khác, 'có thể Exp "bao gồm" NumExp?'. Nhưng từ câu trả lời của bạn, câu trả lời là không. Cảm ơn! – Mau

+2

@Mau: Tôi đã thêm một số thông tin về việc chia sẻ các trường hợp giữa các công đoàn bị phân biệt đối xử khác nhau. Nó không phải là có thể, nhưng bạn có thể bao gồm một trong khác. –

+0

cảm ơn, đó là chính xác những gì tôi đã làm :-) – Mau

-1

Chỉ cần quan sát: Tại sao bạn cần các công đoàn được xây dựng theo cách này?

tôi sẽ chọn một trong hai lựa chọn:

type NumExp = Num of float 

type Exp = 
    | Num of float 
    | Dot of float * float 
    | Op of string * Exp * Exp 

đó là đơn giản hơn, hoặc

type NumExp = Num of float 

type Exp = 
    | NumExp 
    | Dot of float * float 
    | Op of string * Exp * Exp 

Trong trường hợp thứ hai này, chức năng của bạn

let getValue (Num(n) : NumExp) = n 

công trình như bạn có một định nghĩa là NumExp bây giờ.

+0

Cảm ơn Muhammad. Giải pháp đầu tiên được loại trừ trong câu hỏi trên (khó giải quyết hơn trong phần còn lại của mã). Giải pháp thứ hai là chính xác và ngữ nghĩa những gì tôi muốn, nhưng tạo ra một cấu trúc dữ liệu khác với những gì tôi cần ('Exp.NumExp' chỉ là một trường hợp trống, không phải là trường kiểu' NumExp'). – Mau

2

Khi điều đó có thể (ví dụ: sử dụng các biến thể đa hình trong OCaml), bạn có thể làm rất nhiều với nó nhưng (đáng buồn) F # không có tính năng ngôn ngữ này vì vậy hiện không có khả năng thể hiện những gì bạn muốn bằng cách sử dụng các loại công đoàn. Tuy nhiên, bạn có thể cân nhắc sử dụng OOP thay thế ...

2

Bạn có thể sử dụng interfaces as a substitute. Điều này cho biết thêm một chút chi phí cú pháp, nhưng là cách tốt nhất tôi đã tìm thấy để làm điều này.

type IExp = interface end 

type NumExp = 
     | Num of float 
     interface IExp 
type Exp = 
     | Dot of NumExp * NumExp 
     | Op of string * IExp * IExp 
     interface IExp 

// This function accepts both NumExp and Exp 
let f (x:IExp) = match x with 
    | :? NumExp as e -> match e with 
     | Num v -> "Num" 
    | :? Exp as e -> match e with 
     | Dot (e1,e2) -> "Dot" 
     | Op (op,e1,e2) -> "Op" 
    | _ -> invalidArg "x" "Unsupported expression type" 

// This function accepts only NumExp 
let g = function 
    | Num v -> "Num" 
+1

Uhmm Tôi không chắc chắn tôi làm theo. Làm thế nào để giải quyết vấn đề này bằng cách sử dụng chữ số 'Num' trong cả hai công đoàn *? – Mau

+1

Bạn có thể sử dụng 'IExp' khi bạn muốn sử dụng cả DU (kết hợp 3 trường hợp) và 'NumExp' khi bạn chỉ muốn sử dụng' Num'. Hàm 'f' trong ví dụ về hàm sử dụng/chấp nhận cả 3 trường hợp. Về cơ bản, bạn đã tạo ra IExp' DU với 3 trường hợp và 'Exp' chỉ có ở chế độ nền. – dtech

+1

Tôi hiểu rồi! Thực sự tốt đẹp! – Mau

Các vấn đề liên quan