F# 用F折叠列表#

F# 用F折叠列表#,f#,F#,我有一个非常琐碎的任务,但我不知道如何使解决方案更漂亮。 目标是获取列表并根据结果是否传递谓词返回结果。结果应该分组。下面是一个简化的示例: 谓词:isEven Inp:[2;4;3;7;6;10;4;5] Out:[[^^^^^]..[^^^^^^^]..] 以下是我目前掌握的代码: let f p ls = List.foldBack (fun el (xs, ys) -> if p el then (el::xs, ys) else ([], xs::ys))

我有一个非常琐碎的任务,但我不知道如何使解决方案更漂亮。
目标是获取
列表
并根据结果是否传递谓词返回结果。结果应该分组。下面是一个简化的示例:

谓词:
isEven

Inp:[2;4;3;7;6;10;4;5]

Out:[[^^^^^]..[^^^^^^^]..]

以下是我目前掌握的代码:

let f p ls =
    List.foldBack
        (fun el (xs, ys) -> if p el then (el::xs, ys) else ([], xs::ys))
        ls ([], [])
    |> List.Cons // (1)
    |> List.filter (not << List.isEmpty) // (2)

let even x = x % 2 = 0

let ret =
    [2; 4; 3; 7; 6; 10; 4; 5]
    |> f even
// expected [[2; 4]; [6; 10; 4]]
让f p ls=
List.foldBack
(fun el(xs,ys)->如果p el那么(el::xs,ys)else([],xs::ys))
ls([],[])
|>列表.Cons/(1)
|>List.filter(非f偶数)
//预期的,预期的

这段代码看起来不太可读。另外,我不喜欢第(1)行和第(2)行。有更好的解决方案吗?

这是一个基于递归
列表的递归解决方案。过滤器

let rec _f p ls =
    match ls with
    |h::t -> if p(h) then 
                 match  f p t with
                 |rh::rt -> (h::rh)::rt
                 |[] -> (h::[])::[]
             else []::f p t
    |[] -> [[]]

let  f p ls = _f p ls |> List.filter (fun t -> t <> [])
let rec\f p ls=
匹配
|h::t->如果p(h)那么
与f p t匹配
|右::右->(h::右)::右
|[]->(h::[]):[]
else[]::f p t
|[] -> [[]]
让f p ls=_fp ls |>List.filter(fun t->t[])

不过,最后必须进行筛选似乎并不雅致。

我想不出一种方法可以使用高阶函数优雅地完成这项工作,但这里有一个解决方案,它使用列表理解。我认为它非常简单易懂

设f p ls=
让rec循环xs=
[将xs与
| [] -> ()
|当px->
let group,rest=collectGroup[x]xs
产量组
让开!休息
|_uxS::xs->yield!loop xs]
和组acc=功能
|x::xs当px->collectGroup(x::acc)xs时
|xs->List.rev acc,xs
环路ls

给你。这个函数也应该有相当好的性能

let groupedFilter (predicate : 'T -> bool) (list : 'T list) =
    (([], []), list)
    ||> List.fold (fun (currentGroup, finishedGroups) el ->
        if predicate el then
            (el :: currentGroup), finishedGroups
        else
            match currentGroup with
            | [] ->
                [], finishedGroups
            | _ ->
                // This is the first non-matching element
                // following a matching element.
                // Finish processing the previous group then
                // add it to the finished groups list.
                [], ((List.rev currentGroup) :: finishedGroups))
    // Need to do a little clean-up after the fold.
    |> fun (currentGroup, finishedGroups) ->
        // If the current group is non-empty, finish it
        // and add it to the list of finished groups.
        let finishedGroups =
            match currentGroup with
            | [] -> finishedGroups
            | _ ->
                (List.rev currentGroup) :: finishedGroups

        // Reverse the finished groups list so the grouped
        // elements will be in their original order.
        List.rev finishedGroups;;
这个怎么样

let folder p l = function
    | h::t when p(l) -> (l::h)::t
    | []::_ as a -> a
    | _ as a -> []::a

let f p ls =
    ls
    |> List.rev
    |> List.fold (fun a l -> folder p l a) [[]]
    |> List.filter ((<>) [])
let folder p l=函数
|当p(l)->(l::h)::t
|[]:u作为a->a
|_u作为->[]::a
设f-p-ls=
ls
|>List.rev
|>List.fold(fun a l->folder p l a)[[]]
|>List.filter(()[]))

至少文件夹是非常清晰有效的,但是你需要通过列表反转来为此付出代价。

这是我的看法。你首先需要一些帮助功能:

// active pattern to choose between even and odd intengers
let (|Even|Odd|) x = if (x % 2) = 0 then Even x else Odd x

// fold function to generate a state tupple of current values and accumulated values
let folder (current, result) x =
    match x, current with
    | Even x, _ -> x::current, result // even members a added to current list
    | Odd x, [] -> current, result    // odd members are ignored when current is empty
    | Odd x, _ -> [], current::result // odd members starts a new current

// test on data
[2; 4; 3; 7; 6; 10; 4; 5]
    |> List.rev                             // reverse list since numbers are added to start of current
    |> List.fold folder ([], [])            // perform fold over list
    |> function | [],x -> x | y,x -> y::x   // check that current is List.empty, otherwise add to result

由于列表颠倒,我想转到#seq而不是list

这个版本在内部使用了变异(gasp!)来提高效率,但由于seq的开销,可能会稍微慢一点

let f p (ls) = seq {
    let l = System.Collections.Generic.List<'a>()
    for el in ls do
      if p el then 
        l.Add el
      else 
        if l.Count > 0 then yield l |> List.ofSeq
        l.Clear()
    if l.Count > 0 then yield l |> List.ofSeq
   }
设f p(ls)=seq{

让l=System.Collections.Generic.ListHmm…坦率地说,它看起来并没有太好:
Cons
模式可能会影响性能,最终筛选本质上是一样的。@svick——用C#实现一个命令式解决方案。上面的代码在功能上等同于最自然的C#解决方案。如果最后一组物品是匹配的。@Daniel对,你是。修复了它。这真的让它更难看了。:)这太糟糕了。尽管如此,这仍然是这里最简单的解决方案。您的编辑完全改变了问题。我建议您回滚编辑,选择原始问题的最佳答案,并将编辑作为一个单独的问题,如果您仍然需要社区反馈的话。@Daniel我觉得您在查看答案时是正确的。看到了吗ms everyone对我的问题的理解与我不同。当然,这是我的错,因为我没有首先强调可读性。谢谢你的建议。