我想实现类似虚构的Array.multipick
:
Array.multipick : choosers:('a -> bool) [] -> array:'a [] -> 'a []
在内部,我们使用所有choosers
测试每个数组的元素,从chooser
数组中删除第一个true
返回choosers
,然后我们添加chooser
对结果的论证。之后,我们继续进行交互,而choosers
数组中有剩余元素。
最后一部分很重要,因为没有提前退出要求,只需Array.fold
即可解决。
这可以通过以下方式轻松实现:
let rec impl currentIndex currentChoosers results
但这对我的口味来说太过程序化了。也许有更优雅的解决方案?
答案 0 :(得分:3)
使用更改大小的数组编写优雅代码非常困难。这里有一些代码适用于列表而不会改变任何值。
let rec pick accum elem tried = function
| [] -> (accum, List.rev tried)
| chooser :: rest ->
if chooser elem then (elem :: accum, List.rev_append tried rest)
else pick accum elem (chooser :: tried) rest
let rec multipick_l accum choosers list =
match choosers, list with
| [], _
| _, [] -> List.rev accum
| _, elem :: elems ->
let (accum', choosers') = pick accum elem [] choosers in
multipick_l accum' choosers' elems
let multipick choosers array =
Array.of_list
(multipick_l [] (Array.to_list choosers) (Array.to_list array))
如果您认为除了提前退出要求之外Array.fold_left
可用,您可以使用例外提前退出。
答案 1 :(得分:2)
let multipick (choosers: ('a -> bool) array) (arr: 'a array) : 'a array =
let indexed =
choosers
|> Seq.indexed
|> Map.ofSeq
((indexed, []), arr)
||> foldWhile (fun (cs, res) e ->
if Map.isEmpty cs then
None
else
match cs |> Seq.tryFind (fun kvp -> kvp.Value e) with
| Some kvp -> Some (Map.remove kvp.Key cs, e :: res)
| None -> Some (cs, res))
|> snd
|> List.rev
|> Array.ofList
我使用由数组索引键入的Map
来跟踪剩余的函数 - 这样可以轻松删除元素,但仍保留其顺序(因为映射键值对按键排序)在迭代时)。
由于比较约束,F#Set
不能使用函数。 System.Collections.Generic.HashSet
会起作用,但它是可变的,而且我不确定它是否会保留排序。