相当于重复takeWhile调用:此函数是否具有"标准"名称?

时间:2016-09-12 06:30:24

标签: f# functional-programming

我的情况是标准List.groupBy功能不是我想要的,但我不知道这个功能的正确名称,因此难以搜索对

我有一个'T类型的项目列表,以及一个'T -> 'k密钥生成函数。这些项目已经有点"分组"在列表中一起使用,这样当你通过键功能映射列表时,其结果往往会连续几次具有相同的键,例如, [1; 1; 1; 2; 2; 1; 1; 3; 3; 3; 1; 1]。我想要的是获得一个列表列表,其中内部列表包含密钥生成函数返回相同值的所有项目 - 但它不应该将1的不同序列组合在一起。

换句话说,我的数据是一个字符串列表,密钥生成函数是String.length。所以输入是:

["a"; "e"; "i"; "to"; "of"; "o"; "u"; "and"; "for"; "the"; "I"; "O"]

我想要的输出是:

[["a"; "e"; "i"]; ["to"; "of"]; ["o"; "u"]; ["and"; "for"; "the"]; ["I"; "O"]]

以另一种方式来思考:这就像获取列表的第一项并存储调用键函数的结果一样。然后,您使用takeWhile (fun x -> keyFun x = origKeyFunResult)生成第一个细分。然后当takeWhile停止返回值时,您会记录它何时停止,并且keyFun x的值在第一个值上没有返回原始结果 - 并从那里继续。 (除了那将是O(N * M),其中M是序列的数量,并且在许多情况下将转换为O(N ^ 2) - 而应该可以在O(N)时间内实现该函数)。

现在,我可以很容易地编写该功能。这不是问题。我想知道的是这个函数是否有标准的名称。因为我认为它会被称为groupBy,但那是别的东西。 (List.groupBy String.length会返回[(1, ["a"; "e"; "i"; "o"; "u"; "I"; "O"]); (2, ["to"; "of"]), (3, ["and"; "for"; "the"])],但在这种情况下我想要的是" a / e / i"," o / u"和& #34; I / O"列表保持分离,并且我不希望生成密钥的值返回到输出数据中。

可能没有此功能的标准名称。但如果有,那是什么?

1 个答案:

答案 0 :(得分:0)

我有点晚了,似乎你找到了一个解决方案,而且似乎没有单一的功能可以解决这个问题。

为了应对这一挑战,我试图找到一些可用的解决方案并提出以下建议(无论它们是否有效都是读者需要的):

open System

module List = 
  /// <summary>
  /// Generic List Extension:
  /// Given a comparer function the list will be chunked into sub lists
  /// starting when ever comparer finds a difference.
  /// </summary>
  let chunkByPredicate (comparer : 'T -> 'T -> bool) list = 
    let rec func (i : int, lst : 'T list) : 'T list list =
      if i >= lst.Length then
        List.empty
      else
        let first = lst.[i]
        let chunk = lst |> List.skip(i) |> List.takeWhile (fun s -> comparer first s)
        List.append [chunk] (func((i + chunk.Length), lst))

    func (0, list) |> List.where (fun lst -> not (List.isEmpty lst))

// 1. Using List.fold to chunk by string length
let usingListFold (data : string list) = 
  printfn "1. Using List.fold: "
  data 
  |> List.fold (fun (acc : string list list) s -> 
                  if acc.Length > 0 then
                    let last = acc.[acc.Length - 1]
                    let lastLength = last.[0].Length
                    if lastLength = s.Length then
                      List.append (acc |> List.take (acc.Length - 1)) [(last |> List.append [s])]
                    else
                      List.append acc  [[s]]
                  else
                    [[s]]) ([])

  |> List.iter (printfn "%A")
  printfn ""

// 2. Using List.chunkByPredicate
let usingListChunkByPredicate<'a> (predicate : 'a -> 'a -> bool, data : 'a list) = 
  printfn "2. Using List.chunkByPredicate: "
  data
  |> List.chunkByPredicate predicate
  |> List.iter (printfn "%A")
  printfn ""

[<EntryPoint>]
let main argv = 
  let data = ["a"; "e"; "i"; "to"; "of"; "o"; "u"; "and"; "for"; "the"; "I"; "O"]

  usingListFold data
  usingListChunkByPredicate<string>((fun first s -> first.Length = s.Length), data)

  let intData = [0..50]
  usingListChunkByPredicate<int>((fun first n -> first / 10 = n / 10), intData)

  Console.ReadLine() |> ignore
  0