2

私にはかなり些細な仕事がありますが、ソリューションをより美しくする方法がわかりません。
目標はList、述語に合格したかどうかに基づいて、結果を取得して返すことです。結果をグループ化する必要があります。簡単な例を次に示します。

述語: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]]

このコードはそれほど読みにくいようです。また、(1)と(2)の行は好きではありません。より良い解決策はありますか?

4

6 に答える 6

4

これが私の見解です。最初にいくつかのヘルパー関数が必要です。

// 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
于 2012-09-30T21:54:08.743 に答える
2

これはどう?

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 ((<>) [])

少なくともフォルダは非常に明確で効果的ですが、リストを逆にすることでこの代償を払うことになります。

于 2012-09-30T03:47:47.887 に答える
0

これが再帰に基づく再帰的な解決策ですList.filter

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 <> [])

ただし、最後にフィルタリングする必要があるのはエレガントではないようです。

于 2012-09-30T00:26:21.270 に答える
0

高階関数を使用してこれをエレガントに行う方法は考えられませんが、リスト内包表記を使用した解決策を次に示します。読むのはかなり簡単だと思います。

let f p ls = 
  let rec loop xs = 
    [ match xs with 
      | [] -> ()
      | x::xs when p x -> 
        let group, rest = collectGroup [x] xs
        yield group
        yield! loop rest
      | _::xs -> yield! loop xs ]
  and collectGroup acc = function
    | x::xs when p x -> collectGroup (x::acc) xs
    | xs -> List.rev acc, xs
  loop ls
于 2012-09-30T02:57:24.260 に答える
0

どうぞ。この関数もかなり良いパフォーマンスを発揮するはずです。

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;;
于 2012-09-30T03:33:11.447 に答える
0

リストが逆になっているので、リストの代わりに#seqに行きたいと思います。

このバージョンでは、効率を上げるために内部でミューテーション(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
   }
于 2012-10-01T14:02:11.863 に答える