[英]How to write an F# union type chooser?
Is there a better way to do this if F#? 如果使用F#,还有更好的方法吗?
type T =
| A of int
| B of string
static member chooseA x = match x with A i -> Some i | _ -> None
static member chooseB x = match x with B s -> Some s | _ -> None
The usecase is the following: 用例如下:
let collection = [A 10; B "abc"]
let aItems = collection |> Seq.choose T.chooseA
let bItems = collection |> Seq.choose T.chooseB
Thanks! 谢谢!
Use List.partition to split your source elements: 使用List.partition拆分源元素:
type T =
| A of int
| B of string
let collection = [A 10; B "abc"; A 40; B "120"]
let result = List.partition (function | A _ -> true | _ -> false) collection
val result : T list * T list = ([A 10; A 40], [B "abc"; B "120"])
Then you can use fst and snd to select the relevant lists. 然后,您可以使用fst和snd选择相关列表。
This is awkward, but I can see why it is not an important case F#'s design. 这很尴尬,但是我可以理解为什么它不是F#设计的重要案例。 Usually, there is a solution that allows for a complete pattern match instead of multiple, somewhat incomplete ones. 通常,有一种解决方案允许完整的模式匹配,而不是多个(有些不完整)的模式匹配。 For example, the two concrete item sequences can be constructed like this: 例如,可以这样构造两个具体的项目序列:
let aItems, bItems =
let accA, accB = ResizeArray(), ResizeArray()
collection |> Seq.iter (function A i -> accA.Add i | B s -> accB.Add s)
seq accA, seq accB
A similar solution without mutation can be made if you dislike it, but I see little reason to worry about encapsulated mutation. 如果您不喜欢,可以采用类似的无突变解决方案,但是我几乎没有理由担心封装突变。 Note that the results are cast to seq
. 请注意,结果强制转换为seq
。
This uses pattern matching in the manner it is designed for: 这将按照其设计方式使用模式匹配:
T
, a warning will appear in the handling function, which is exactly where editing should continue: determining how to treat the new input case. 如果将另一个大小写添加到T
,则处理功能中将显示一条警告,这正是继续编辑的地方:确定如何处理新输入的大小写。 If the above isn't suitable, you can still shorten the question's code a bit by using the function
keyword and declaring the chooser function as a lambda. 如果上述方法不合适,您仍然可以通过使用function
关键字并将声明选择器函数声明为lambda来稍微缩短问题的代码。 For example: 例如:
let aItems = collection |> Seq.choose (function A i -> Some i | _ -> None)
Note that this is lazy, just like the proposal in the question: here, every iteration over aItems
will needlessly iterate over all the B
cases in the input. 请注意,这是惰性的,就像问题中的建议一样:在这里,对aItems
每次迭代aItems
将不必要地遍历输入中的所有B
例。
I can offer the following variant: 我可以提供以下变体:
open System.Reflection
type T =
| A of int
| B of string
let collection = [A 10; B "abc"; A 40; B "120"]
let sp (col: T list) (str:string) =
if col=[] then []
else
let names = "Is" + str
col |> List.filter(fun x-> let t = x.GetType()
if t.GetProperty(names) = null then false
else
t.InvokeMember(names, BindingFlags.GetProperty, null, x, null) :?> bool)
|> List.map(fun y ->
y.GetType().InvokeMember("get_Item", BindingFlags.InvokeMethod, null, y, null))
sp collection "A" |> printfn "%A\n"
sp collection "B" |> printfn "%A\n"
sp collection "C" |> printfn "%A\n"
Print: 打印:
[10; 40]
["abc"; "120"]
[]
http://ideone.com/yAytQk http://ideone.com/yAytQk
I'm new to F#, so I think that can be done easier 我是F#的新手,所以我认为这可以轻松完成
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.