优雅的Array.multipick(?)实现

时间:2017-11-19 23:05:56

标签: algorithm f# functional-programming ocaml

我想实现类似虚构的Array.multipick

Array.multipick : choosers:('a -> bool) [] -> array:'a [] -> 'a []

在内部,我们使用所有choosers测试每个数组的元素,从chooser数组中删除第一个true返回choosers,然后我们添加chooser对结果的论证。之后,我们继续进行交互,而choosers数组中有剩余元素

最后一部分很重要,因为没有提前退出要求,只需Array.fold即可解决。

这可以通过以下方式轻松实现:

let rec impl currentIndex currentChoosers results

但这对我的口味来说太过程序化了。也许有更优雅的解决方案?

2 个答案:

答案 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)

提前退出的折叠是一个好主意,但是需要以相当强制的方式编写专门针对数组的具有生产价值的产品。为简单起见,我将从this answer中获取更一般的序列。

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会起作用,但它是可变的,而且我不确定它是否会保留排序。