myt*_*thz 11 linq f# functional-programming sequence
我正在尝试通过将一些C#算法重写为惯用的F#来学习F#.
我正在尝试重写的第一个函数之一是batchesOf,其中:
[1..17] |> batchesOf 5
Run Code Online (Sandbox Code Playgroud)
哪个会将序列分成多个批次,每个最多五个,即:
[[1; 2; 3; 4; 5]; [6; 7; 8; 9; 10]; [11; 12; 13; 14; 15]; [16; 17]]
Run Code Online (Sandbox Code Playgroud)
我第一次尝试这样做有点丑陋,我在尝试在闭包内部使用可变类型时遇到错误而使用了一个可变的ref对象.使用ref特别不愉快,因为要取消引用它你必须使用!在一个条件表达式内部的操作符可以与某些开发人员相反直观,这些开发人员将其视为逻辑不符合.我遇到的另一个问题是Seq.skip和Seq.take与他们的Linq别名不同,如果大小超过序列的大小,他们将抛出错误.
let batchesOf size (sequence: _ seq) : _ list seq =
seq {
let s = ref sequence
while not (!s |> Seq.isEmpty) do
yield !s |> Seq.truncate size |> List.ofSeq
s := System.Linq.Enumerable.Skip(!s, size)
}
Run Code Online (Sandbox Code Playgroud)
无论如何,在F#中重写这个最优雅/惯用的方法是什么?保持原始行为但最好没有ref可变变量.
Tom*_*cek 15
使用seq<_>idiomatically类型实现此函数很困难 - 类型本质上是可变的,因此没有简单的好功能方式.您的版本效率很低,因为它Skip在序列上重复使用.更好的命令选项是使用GetEnumerator并使用迭代元素IEnumerator.您可以在此代码段中找到各种必要选项:http://fssnip.net/1o
如果您正在学习F#,那么最好尝试使用F#list类型编写函数.这样,您就可以使用惯用的功能风格.然后你可以batchesOf使用模式匹配来编写递归和累加器参数,如下所示:
let batchesOf size input =
// Inner function that does the actual work.
// 'input' is the remaining part of the list, 'num' is the number of elements
// in a current batch, which is stored in 'batch'. Finally, 'acc' is a list of
// batches (in a reverse order)
let rec loop input num batch acc =
match input with
| [] ->
// We've reached the end - add current batch to the list of all
// batches if it is not empty and return batch (in the right order)
if batch <> [] then (List.rev batch)::acc else acc
|> List.rev
| x::xs when num = size - 1 ->
// We've reached the end of the batch - add the last element
// and add batch to the list of batches.
loop xs 0 [] ((List.rev (x::batch))::acc)
| x::xs ->
// Take one element from the input and add it to the current batch
loop xs (num + 1) (x::batch) acc
loop input 0 [] []
Run Code Online (Sandbox Code Playgroud)
作为一个脚注,使用计算表达式可以使命令版本更好一些IEnumerator,但这不是标准的,它是非常高级的技巧(例如,见http://fssnip.net/37).
Ash*_*eyF 10
一段时间朋友问过我.这是一个回收的答案.这是有效的,纯粹的:
let batchesOf n =
Seq.mapi (fun i v -> i / n, v) >>
Seq.groupBy fst >>
Seq.map snd >>
Seq.map (Seq.map snd)
Run Code Online (Sandbox Code Playgroud)
或者不纯的版本:
let batchesOf n =
let i = ref -1
Seq.groupBy (fun _ -> i := !i + 1; !i / n) >> Seq.map snd
Run Code Online (Sandbox Code Playgroud)
这些产生了seq<seq<'a>>.如果你'a list list的样本中必须有一个as,那么只需添加... |> Seq.map (List.ofSeq) |> List.ofSeq如下:
> [1..17] |> batchesOf 5 |> Seq.map (List.ofSeq) |> List.ofSeq;;
val it : int list list = [[1; 2; 3; 4; 5]; [6; 7; 8; 9; 10]; [11; 12; 13; 14; 15]; [16; 17]]
Run Code Online (Sandbox Code Playgroud)
希望有所帮助!