我有以下内容:
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>
答案 0 :(得分:8)
您假设您的序列不包含单个case1,因此如果不是这样,则需要抛出异常。
let matchCase item =
match item with
| Case1 x -> failwith "Unexpected Case1"
| Case2 x -> x
let case2s = Seq.map matchCase union1s
另一种方法,如果您不确定序列是否包含始终相同的情况,则使用选项
let matchCase item =
match item with
| Case1 x -> None
| Case2 x -> Some x
然后它取决于你将如何处理这些情况,你可以使用Seq.choose而不是Seq.map过滤出None
值,如另一个答案所示。
如果您考虑将Case1s的参数作为例外情况或程序逻辑的一部分,那么遵循哪种方法取决于哪种方法。最近有一个关于F#: Some, None, or Exception?的问题。
如果您没有混合案例,那么使用DU序列是正确的,这样您的DU类型会将您的域限制为实际案例。
答案 1 :(得分:3)
作为替代方案:
let matchCaseOpt item =
match item with
| Case2 x -> Some(x)
| _ -> None
let case2s = union1s |> Seq.choose matchCaseOpt
此版本将删除除Case2之外的任何情况,如果发生这些情况,Gustavo的解决方案将抛出异常。当然,哪种解决方案最好取决于您的具体要求。
请注意,此解决方案使用Seq.choose而不是Seq.map。
答案 2 :(得分:2)
您可以尝试以下基于反射的通用实现:
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 ]
即使在包含多个字段的联合案例中也是如此。