我有以下内容:
type union1 =
| Case1 of string
| Case2 of int
let union1s = seq { for i in 1..5 do yield case2 i }
如何将union1s
更改为类型为seq<int>
的序列?
类似于:
let matchCase item =
match item with
| Case1 x -> x
| Case2 x -> x
let case2s = Seq.map matchCase union1s
此尝试不起作用,因为matchCase不能返回两个不同的类型。
建议的答案有相同的问题(如果我理解正确的话)
let matchCaseOpt = function
| Case1 x -> Some x
| Case2 x -> Some x
| _ -> None
let case2s = Seq.choose matchCaseOpts unions1s
Some x期望的表达式在匹配Case2 时应为Option字符串类型
我已经通过使用序列的DU解决了我的特定用例。
type Union1s =
| Case1s of seq<string>
| Case2s of seq<int>
您假设您的序列不包含一个case1,所以如果这不是真的,您需要抛出一个异常。
let matchCase item =
match item with
| Case1 x -> failwith "Unexpected Case1"
| Case2 x -> x
let case2s = Seq.map matchCase union1s
如果您不确定序列是否始终包含相同的情况,另一种方法是使用Option
let matchCase item =
match item with
| Case1 x -> None
| Case2 x -> Some x
然后,这取决于您将如何处理这些情况,您可以使用Seq.select而不是如另一个答案所示的Seq.map来过滤None
值。
采用哪种方法取决于您是否将Case1的自变量视为特殊情况或程序逻辑的一部分。有一个关于F#的问题:有的、没有还是例外?不久前
如果你不混合案例,那么使用序列的DU是正确的,这样你的DU类型就会将你的域限制为实际案例。
作为替代方案:
let matchCaseOpt item =
match item with
| Case2 x -> Some(x)
| _ -> None
let case2s = union1s |> Seq.choose matchCaseOpt
这个版本将删除除Case2以外的任何情况,如果发生这些情况,Gustavo的解决方案将抛出异常。当然,哪种解决方案最好取决于您的确切要求。
请注意,此解决方案使用顺序选择而不是顺序映射。
您可以尝试以下基于反射的通用实现:
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 ]
即使在包含多个字段的并集情况下,这也应该有效。