2013-09-04 53 views
5

Tôi đã điều sau đây:Làm thế nào để ánh xạ một chuỗi các công đoàn phân biệt đối xử (trong đó tất cả các mục của cùng một trường hợp) lên một chuỗi các mục của kiểu vụ án?

type union1 = 
    | Case1 of string 
    | Case2 of int 

let union1s = seq { for i in 1..5 do yield case2 i } 

Làm thế nào để thay đổi union1s đến một chuỗi các loại seq<int>?

Cái gì như:

let matchCase item = 
    match item with 
    | Case1 x -> x 
    | Case2 x -> x 

let case2s = Seq.map matchCase union1s 

nỗ lực này không làm việc vì matchCase không thể trở về hai loại khác nhau.

Những câu trả lời gợi ý có cùng một vấn đề (nếu tôi hiểu đúng)

let matchCaseOpt = function 
    | Case1 x -> Some x 
    | Case2 x -> Some x 
    | _ -> None 

let case2s = Seq.choose matchCaseOpts unions1s 

Khái niệm Một số x hy vọng hy vọng gõ Lựa chọn chuỗi trong trận đấu cho Trường hợp2

tôi đã giải quyết tình huống sử dụng cụ thể của tôi bằng cách sử dụng DU của chuỗi.

type Union1s = 
    | Case1s of seq<string> 
    | Case2s of seq<int>  
+0

Xin lưu ý đó không phải là câu trả lời được đề xuất. Trường hợp 1 sai, nên là Không. – Gustavo

Trả lời

2

Bạn có thể thử sự phản ánh dựa trên, thực hiện mục đích chung sau đây:

open Microsoft.FSharp.Quotations 
open Microsoft.FSharp.Quotations.Patterns 
open Microsoft.FSharp.Reflection 

let filterUnionCases (branch : Expr<'T -> 'Union>) (inputs : 'Union list) = 
    let rec getUnionCase (e : Expr) = 
     match e with 
     | NewUnionCase(unionCaseInfo,_) -> unionCaseInfo 
     | Lambda(_, body) -> getUnionCase body 
     | Let(_, TupleGet _, body) -> getUnionCase body 
     | _ -> invalidArg "branch" "not a union case constructor" 

    let getBranchContents (uci : UnionCaseInfo) (u : 'Union) = 
     let uci', fields = FSharpValue.GetUnionFields(u, typeof<'Union>) 
     if uci = uci' then 
      match fields with 
      | [| field |] -> field :?> 'T 
      | _ -> FSharpValue.MakeTuple(fields, typeof<'T>) :?> 'T 
      |> Some 
     else None 

    let uci = getUnionCase branch 
    inputs |> List.choose (getBranchContents uci) 


filterUnionCases <@ Case1 @> [ Case1 "string1" ; Case2 2 ; Case1 "string2" ] // [ "string1" ; "string2" ] 
filterUnionCases <@ Case2 @> [ Case1 "string1" ; Case2 2 ; Case1 "string2" ] // [ 2 ] 

này nên làm việc ngay cả trong trường hợp công đoàn có chứa nhiều lĩnh vực.

+0

Cảm ơn. Tôi có thể làm theo những gì đang xảy ra, nhưng nó chắc chắn kéo dài những gì tôi đang sử dụng bây giờ. Lô cần tìm hiểu – Remko

+0

Bạn nên nhớ rằng đây là * chậm *, vì nó sử dụng sự phản chiếu. Nếu hiệu suất là quan trọng với bạn, bạn nên đi cho các giải pháp được cung cấp bởi các áp phích khác. – eirik

+0

Tôi sẽ, tôi đã phá vỡ vấn đề của mình bằng cách sử dụng DU của chuỗi. Cảm ơn một lần nữa – Remko

8

Bạn đang giả định trình tự của bạn không chứa một trường hợp duy nhất1, vì vậy nếu điều này không đúng, bạn cần phải ném ngoại lệ.

let matchCase item = 
    match item with 
    | Case1 x -> failwith "Unexpected Case1" 
    | Case2 x -> x 

let case2s = Seq.map matchCase union1s 

Một cách tiếp cận khác, nếu bạn không chắc chắn chuỗi chứa luôn luôn như vậy cùng là sử dụng một lựa chọn

let matchCase item = 
    match item with 
    | Case1 x -> None 
    | Case2 x -> Some x 

Sau đó, nó phụ thuộc vào cách bạn sẽ xử lý những trường hợp này, bạn chỉ có thể lọc các giá trị None sử dụng Seq.choose thay vì Seq.map như được hiển thị trên câu trả lời khác.

Phương pháp tiếp cận phụ thuộc nào nếu bạn cân nhắc việc tranh luận với Case1 là trường hợp ngoại lệ hoặc một phần logic của chương trình của bạn. Có một câu hỏi liên quan đến số này F#: Some, None, or Exception? gần đây.

Sử dụng DU chuỗi là chính xác nếu bạn không trộn các trường hợp, theo cách đó, loại DU của bạn hạn chế miền của bạn đối với các trường hợp thực tế.

+0

Xin chào, cảm ơn. Tuy nhiên đôi khi seq chứa các mục chỉ có case1 – Remko

+0

Sau đó sử dụng phiên bản thay thế và kiểm tra kết quả. Bạn có thể quyết định sau đó phải làm gì với các NONE. Có thể lọc chúng ra bằng Seq.choose như được hiển thị trên câu trả lời khác.Nó thực sự phụ thuộc nếu nó là một trường hợp ngoại lệ hay không. – Gustavo

+0

câu hỏi cập nhật – Remko

3

Là một thay thế:

let matchCaseOpt item = 
    match item with 
    | Case2 x -> Some(x) 
    | _ -> None 

let case2s = union1s |> Seq.choose matchCaseOpt 

Phiên bản này sẽ thả bất kỳ trường hợp khác ngoài Trường hợp2, nơi giải pháp Gustavo sẽ ném một ngoại lệ nếu những xảy ra. Giải pháp nào là tốt nhất phụ thuộc vào các yêu cầu chính xác của bạn, tất nhiên.

Lưu ý rằng giải pháp này sử dụng Seq.choose thay vì Seq.map.