F#:从 seq 中删除重复项很慢

2023-11-24

我正在尝试编写一个函数,根据给定的相等函数确定,从seq<'a>但有一个转折:我需要last从一系列重复项中进行复制,使其进入结果序列。例如,如果我有一个序列[("a", 1); ("b", 2); ("b", 3); ("b", 4); ("c", 5)],我正在使用fun ((x1, y1),(x2, y2)) -> x1=x2检查是否相等,我想看到的结果是[("a", 1); ("b", 4); ("c", 5)]。这个函数的要点是我有数据点进来,有时数据点合法地具有相同的时间戳,但我只关心最新的一个,所以我想扔掉前面具有相同时间戳的数据点。我实现的功能如下:

let rec dedupeTakingLast equalityFn prev s = seq {
     match ( Seq.isEmpty s ) with
     | true -> match prev with 
                 | None -> yield! s
                 | Some value -> yield value
     | false ->
         match prev with 
         | None -> yield! dedupeTakingLast equalityFn (Some (Seq.head s)) (Seq.tail s) 
         | Some prevValue -> 
             if not (equalityFn(prevValue, (Seq.head s))) then 
                 yield prevValue
             yield! dedupeTakingLast equalityFn (Some (Seq.head s)) (Seq.tail s)
}

就实际工作而言,它是有效的:

> [("a", 1); ("b", 2); ("b", 3); ("b", 4); ("c", 5)] 
  |> dedupeTakingLast (fun ((x1, y1),(x2, y2)) -> x1=x2) None 
  |> List.ofSeq;;
val it : (string * int) list = [("a", 1); ("b", 4); ("c", 5)]

然而,就性能而言,这是一场灾难:

> #time
List.init 1000 (fun _ -> 1) 
|> dedupeTakingLast (fun (x,y) -> x = y) None 
|> List.ofSeq
#time;;    
--> Timing now on    
Real: 00:00:09.958, CPU: 00:00:10.046, GC gen0: 54, gen1: 1, gen2: 1
val it : int list = [1]    
--> Timing now off

显然我在这里做了一些非常愚蠢的事情,但我看不出它是什么。性能损失从何而来?我意识到有更好的实现,但我特别想了解为什么this实施速度如此之慢。

编辑:仅供参考,设法以依赖于的功能风格实现了一个不错的实现Seq.仅功能。性能还不错,大约是命令式实现时间的 1.6 倍@gradbot下面使用迭代器。看来问题的根源在于使用Seq.head and Seq.tail在我最初的努力中的递归调用中。

let dedupeTakingLastSeq equalityFn s = 
    s 
    |> Seq.map Some
    |> fun x -> Seq.append x [None]
    |> Seq.pairwise
    |> Seq.map (fun (x,y) -> 
            match (x,y) with 
            | (Some a, Some b) -> (if (equalityFn a b) then None else Some a)  
            | (_,None) -> x
            | _ -> None )
    |> Seq.choose id

性能问题来自对 Seq.tail 的嵌套调用。这是源代码Seq.tail

[<CompiledName("Tail")>]
let tail (source: seq<'T>) =
    checkNonNull "source" source
    seq { use e = source.GetEnumerator() 
          if not (e.MoveNext()) then 
              invalidArg "source" (SR.GetString(SR.notEnoughElements))
          while e.MoveNext() do
              yield e.Current }

如果你打电话Seq.tail(Seq.tail(Seq.tail(...)))编译器无法优化由以下方法创建的枚举器GetEnumerator()。后续返回的元素必须遍历每个嵌套序列和枚举器。这导致每个返回的元素都必须冒泡通过所有先前创建的序列,并且所有这些序列也必须增加其内部状态。最终结果是运行时间为 O(n^2),而不是线性 O(n)。

不幸的是,目前还没有办法在 F# 中以函数式风格表示这一点。您可以使用列表 (x::xs),但不能使用序列。在语言获得更好的序列原生支持之前,不要在递归函数中使用 Seq.tail。

使用单个枚举器将解决性能问题。

let RemoveDuplicatesKeepLast equals (items:seq<_>) =
    seq {
        use e = items.GetEnumerator()

        if e.MoveNext() then
            let mutable previous = e.Current

            while e.MoveNext() do
                if not (previous |> equals e.Current) then 
                    yield previous
                previous <- e.Current

            yield previous
    }

let test = [("a", 1); ("b", 2); ("b", 3); ("b", 4); ("c", 5)]
let FirstEqual a b = fst a = fst b

RemoveDuplicatesKeepLast FirstEqual test
|> printf "%A"

// output
// seq [("a", 1); ("b", 4); ("c", 5)]

该答案的第一个版本具有上述代码的递归版本,没有突变。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

F#:从 seq 中删除重复项很慢 的相关文章

随机推荐

  • Thread.sleep 等待时间超出预期

    以下代码 long msBefore System currentTimeMillis Thread currentThread setPriority Thread MAX PRIORITY try Thread sleep 200 ca
  • 查找 MySQL JSON 对象或数组的交集

    问题是关于MySQL MariaDB JSON 函数 如何找到多个 JSON 结构的交集 在 PHP 中 它是使用以下代码完成的 array intersect a b b c 如果我们想象一个名为 JSON INTERSECT 的函数 代
  • 接受来自 scanf 函数的任意数量的输入

    我正在尝试使用读取未知数量的输入scanf功能 int a 100 int i 0 while scanf d a i n i Next part of the code 但是这个函数不会进入代码的下一部分 似乎有一个无限的 while 循
  • Spring - 计划任务 - 优雅关机

    我有一个 Spring Boot 应用程序 其中有一个 Bean 以大约 1 分钟的间隔运行计划任务 并且该 Bean 有一个 PreDestroy方法 是否有解决方案允许当前正在执行的任务在生命周期到达预销毁阶段之前完成 或者至少给定一些
  • 如何管理 AngularJS 中加载指令模板的 404 错误

    在 AngularJS 指令中templateUrl参数是动态定义的 templates content id html 我不想建立规则来检查是否content id值有效并将其管理为 404 错误 即如果模板不存在 服务器在加载模板时返回
  • 如何区分InputBox取消和确定按钮?

    快速提问 我正在使用一个Microsoft VisualBasic Interaction InputBox在我的 C 代码中允许用户将网站添加到列表中 但我不希望他们输入空字符串 因此我会弹出错误窗口 以防发生这种情况 但是 如果用户按
  • 如何删除向量的每个第三个元素?

    我有以下向量 myList c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 如何删除每个第三个元素 不是这样的 myList myList 3 myList myList 6 我需要以下输出 1 2 4 5 7
  • 无法在AWS Lambda函数上使用ES6;如何在 Lambda 中导入 ES6 模块

    我有一个图书馆foo这是用 ES6 编写的 import export并在打字稿中 我有一个应用程序bar它使用foo bar也是用导出和 Typescript 编写的 我想得到bar在 AWS Lambda 上运行 据我所知 我不能使用i
  • =+ Python 运算符语法正确

    我无意中写道 total acc accuracy 代替 total acc accuracy 我在网上搜索没有找到anything 那么发生了什么 为什么 Python 认为我正在输入的内容是什么意思 Computers trust us
  • 从 python BeautifulSoup 的输出中删除新行 '\n'

    我正在使用 python Beautiful soup 来获取以下内容 div class path a href abc a a href def a a href ghi a div 我的代码如下 html doc div class
  • 使用 Google-Maps-iOS-SDK (1.8.1) 时架构 armv7 的未定义符号

    我正在尝试添加使用 cocoapods 0 33 1 的 Google Maps iOS SDK 1 8 1 部署目标版本 iOS 7 0 我添加了这个 Pod pod Google Maps iOS SDK gt 1 8 正确下载并安装了
  • 如何在没有 Storyboard 的情况下在 UITableViewRowAction 中自定义字体和颜色

    我有经典的 TableView 如果您滑动并单击按钮 您可以删除项目 我知道如何在单元格上设置自定义背景 但我找不到如何为其设置自定义字体和颜色 谢谢你的帮助 func tableView tableView UITableView edi
  • java 中的 Servlet - getWriter() 和 getOutputStream()

    为什么在一个实例上ServletResponse both getWriter and getOutputStream 不能被调用吗 一个设计决定 Writer 和 OutputStream 都维护自己的缓冲区 如果您分别创建一个 那么它们
  • 从 ArrayList 创建格式化字符串

    考虑以下代码 ArrayList
  • Unity 的 Mathf.PingPong 实际上是做什么的?

    Unity 文档用于数学乒乓球 says 乒乓球的价值t 因此它永远不会大于length并且永远不会小于0 我知道它正在 0 到 0 之间旋转一个值length 我不明白的是价值是什么t它与 PingPong 的运作方式有何关系 如果我设置
  • 枚举与架构不匹配:jaxb 或 xsd 有问题吗?

    我正在尝试使用 JAXB 来解组这个文件转换为 Java 对象 我知道 J6 中的 SAX 有一个问题 拒绝 maxOccurs 行 我已将其更改为unbounded 然而 当我xjc它 它没有创建我需要的所有类和枚举 例如 应该有一个ed
  • 命令行 perl 脚本中的进度条

    我正在尝试在命令提示符中以 形式打印进度 但它无法正常工作 我想将进度打印为 Status 10 Completed 当 20 完成时 它将显示状态 20 已完成 在同一个地方而不是在新行中 请你帮助我好吗 Code count per c
  • DataTables 插件 - 在 tfoot 标签下方显示滚动条?

    我使用 jQuery DataTables 插件 scrollX true用于水平滚动 为什么上面会出现滚动条tfoot标签 如何让它出现在页脚下方 var table example DataTable scrollX true scro
  • Django 表单集分页

    我有一个模型表单集 我想使用 Django 的 Paginator 一次显示 10 个表单 但不能像这样完成paginator Paginator formset 10 如果有办法的话 正确的方法是什么 这是我发现的问题解决方案的通用示例
  • F#:从 seq 中删除重复项很慢

    我正在尝试编写一个函数 根据给定的相等函数确定 从seq lt a gt 但有一个转折 我需要last从一系列重复项中进行复制 使其进入结果序列 例如 如果我有一个序列 a 1 b 2 b 3 b 4 c 5 我正在使用fun x1 y1