在 F# 中编写 batchesOf size seq 的最惯用方法 [英] Most idiomatic way to write batchesOf size seq in F#

查看:21
本文介绍了在 F# 中编写 batchesOf size seq 的最惯用方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过将一些 C# 算法重写为惯用的 F# 来学习 F#.

I'm trying to learn F# by rewriting some C# algorithms I have into idiomatic F#.

我尝试重写的第一个函数是一个 batchesOf,其中:

One of the first functions I'm trying to rewrite is a batchesOf where:

[1..17] |> batchesOf 5

这会将序列分成多个批次,每个批次最多五个,即:

Which would split the sequence into batches with a max of five in each, i.e:

[[1; 2; 3; 4; 5]; [6; 7; 8; 9; 10]; [11; 12; 13; 14; 15]; [16; 17]]

我第一次尝试这样做有点难看,在尝试在闭包中使用 mutable 类型时遇到错误后,我求助于使用可变的 ref 对象.使用 ref 尤其令人不快,因为要取消引用它,您必须使用 ! 运算符,当在条件表达式中时,对于某些将其读作 的开发人员来说可能是违反直觉的>逻辑不.我遇到的另一个问题是 Seq.skip 和 Seq.take 不像它们的 Linq 别名,因为如果 size 超过序列的大小,它们会抛出错误.

My first attempt at doing this is kind of ugly where I've resorted to using a mutable ref object after running into errors trying to use mutable type inside the closure. Using ref is particularly unpleasant since to dereference it you have to use the ! operator which when inside a condition expression can be counter intuitive to some devs who will read it as logical not. Another problem I ran into is where Seq.skip and Seq.take are not like their Linq aliases in that they will throw an error if size exceeds the size of the sequence.

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)
    }

无论如何,在 F# 中重写它的最优雅/惯用的方法是什么?保持原始行为,但最好不要使用 ref 可变变量.

Anyway what would be the most elegant/idiomatic way to rewrite this in F#? Keeping the original behaviour but preferably without the ref mutable variable.

推荐答案

使用 seq<_> 类型惯用地实现此功能是困难的 - 该类型本质上是可变的,因此没有简单的 nice功能方式.你的版本效率很低,因为它在序列上重复使用 Skip .更好的命令选项是使用 GetEnumerator 并使用 IEnumerator 迭代元素.您可以在此代码段中找到各种命令式选项:http://fssnip.net/1o

Implementing this function using the seq<_> type idiomatically is difficult - the type is inherently mutable, so there is no simple nice functional way. Your version is quite inefficient, because it uses Skip repeatedly on the sequence. A better imperative option would be to use GetEnumerator and just iterate over elements using IEnumerator. You can find various imperative options in this snippet: http://fssnip.net/1o

如果您正在学习 F#,那么最好尝试使用 F# 列表类型编写函数.这样,您就可以使用惯用的功能样式.然后你可以使用带有递归和累加器参数的模式匹配来编写 batchesOf ,如下所示:

If you're learning F#, then it is better to try writing the function using F# list type. This way, you can use idiomatic functional style. Then you can write batchesOf using pattern matching with recursion and accumulator argument like this:

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 [] []

作为脚注,使用计算表达式可以更好地使用IEnumerator,但那不是标准的,它是相当高级的技巧(对于例如,请参见 http://fssnip.net/37).

As a footnote, the imperative version can be made a bit nicer using computation expression for working with IEnumerator, but that's not standard and it is quite advanced trick (for example, see http://fssnip.net/37).

这篇关于在 F# 中编写 batchesOf size seq 的最惯用方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆