重复takeWhile调用的等价物:此函数是否具有"标准"名称?

rmu*_*unn 5 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"]
Run Code Online (Sandbox Code Playgroud)

我正在寻找的输出将是:

[["a"; "e"; "i"]; ["to"; "of"]; ["o"; "u"]; ["and"; "for"; "the"]; ["I"; "O"]]
Run Code Online (Sandbox Code Playgroud)

以另一种方式来思考:这就像获取列表的第一项并存储调用键函数的结果一样.然后你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"和"I/O"列表保持分离,我不希望键的值 - 生成返回到输出数据中).

也许这个功能没有标准名称.但如果有,那是什么?

小智 0

我有点晚了,看来你已经找到了解决方案,而且 F# 似乎不存在可以处理该问题的单个函数。

为了应对挑战,我试图找到一些可用的解决方案,并提出了以下解决方案(它们是否有效由读者决定):

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
Run Code Online (Sandbox Code Playgroud)