在F#中寻找Haskell`group`替代品

时间:2014-05-29 07:37:43

标签: f#

Haskell group在那里描述:http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html#v:group

示例:

group "Mississippi" = ["M","i","ss","i","ss","i","pp","i"]

有简单的F#方式吗?

我可以实现类似的东西

let rec sameCounter (data : list<float>) el same (ss : list<float * int>) = 
    if data.Length > (el + 1) then
        if data.[el] = data.[el + 1] then
                              sameCounter data (el + 1) <| same + 1  <| ss
        else if same > 0 then sameCounter data (el + 1) <| 0         <| (data.[el], same) :: ss
             else             sameCounter data (el + 1) <| 0         <| ss
    else ss
let group d = sameCounter d 0 0 []

但我认为这有点难看,是否有更好的变体?

5 个答案:

答案 0 :(得分:7)

这是我提出的,我认为这是合理的:

let groupConsecutive sq =
    (Array.ofSeq sq, [])
    ||> Array.foldBack (fun x -> function
        | [] -> [[x]]
        | xs :: xss ->
            if x = List.head xs then
                (x :: xs) :: xss
            else [x] :: xs :: xss)
// val groupConsecutive : s:seq<'a> -> 'a list list when 'a : equality

groupConsecutive "Mississippi"
// val it : char list list = [['M']; ['i']; ['s'; 's']; ['i']; ['s'; 's']; ['i']; ['p'; 'p']; ['i']]

如果您还希望能够接收和返回无限序列,那么您需要与GetEnumerator手工合作,这会变得更加丑陋。

答案 1 :(得分:3)

这是一个懒惰的版本。

open System.Collections.Generic

let groupConsecutive (sq : seq<_>) =
    let rec group (e : IEnumerator<_>) xs =
        seq {
            if e.MoveNext() then
                if e.Current = List.head xs then
                    yield! group e (e.Current :: xs)
                else
                    yield List.rev xs
                    yield! group e [e.Current]
            else
                yield List.rev xs
        }

    seq {
        let e = sq.GetEnumerator()
        if e.MoveNext() then
            yield! group e [e.Current]
    }

答案 2 :(得分:2)

作为替代方案,这里是spangroupBy的原始haskell源实现的相当字面翻译。条件是需要继续k以确保尾递归。

let span p = 
    let rec loop k = function
    | x :: xs' when p x ->
        let f (ys, zs) = x :: ys, zs
        loop (f >> k) xs'
    | xs -> k ([], xs)
    loop id

let groupBy eq = 
    let rec loop k = function
    | [] -> k []
    | x :: xs ->
        let (ys, zs) = span (eq x) xs
        let f zss = (x :: ys) :: zss
        loop (f >> k) zs
    loop id

//Special case of 'groupBy', group by equality.
let group xs = groupBy (=) xs

group <| List.ofSeq "Mississippi" 
// val it : char list list =
//   [['M']; ['i']; ['s'; 's']; ['i']; ['s'; 's']; ['i']; ['p'; 'p']; ['i']]

答案 3 :(得分:1)

可能与列表和模式匹配?

let group s =
    let rec loop = function  
        | x::xs, [] -> loop (xs, [x])                      // First element
        | x::xs, s::ss when x = s -> loop (xs, s::s::ss)   // Same
        | x::xs, ss -> ss :: loop (xs, [x])                // Different
        | [], ss -> [ss]                                   // Terminate
    loop (List.ofSeq s, [])

group "Mississippi"

这不会赢得任何效率奖,但它(合理地)清楚。

答案 4 :(得分:-1)

也许:

Seq.groupBy

  

将键生成函数应用于序列的每个元素,并生成一系列唯一键和包含每个键的所有元素的序列。

注意:Haskell中的group只是groupBy (==)