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)