首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在列表中查找重复元素的基本功能是什么?

在列表中查找重复元素的基本功能是什么?
EN

Stack Overflow用户
提问于 2016-04-06 15:34:04
回答 4查看 2.3K关注 0票数 7

在列表中查找重复元素的基本功能是什么?

翻译后,如何简化以下功能:

代码语言:javascript
运行
复制
let numbers = [ 3;5;5;8;9;9;9 ]

let getDuplicates = numbers |> List.groupBy id
                            |> List.map snd
                            |> List.filter (fun set -> set.Length > 1)
                            |> List.map (fun set -> set.[0])

我肯定这是个复制品。但是,我无法在这个网站上找到这个问题。

更新

代码语言:javascript
运行
复制
let getDuplicates numbers =

    numbers |> List.groupBy id
            |> List.choose (fun (k,v) -> match v.Length with
                                         | x when x > 1 -> Some k
                                         | _            -> None)
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-04-06 20:59:16

简化了函数

每当您有一个过滤器和一个映射之后,您可能可以用一个选择替换这两个过滤器。选择的目的是为列表中的每个值运行一个函数,并且只返回返回某些值的项(不移除任何值,这是筛选器部分)。无论您在其中放置了什么值,都是地图部分:

代码语言:javascript
运行
复制
let getDuplicates = numbers |> List.groupBy id
                            |> List.map snd
                            |> List.choose( fun( set ) ->
                               if set.Length > 1
                                  then Some( set.[0] )
                                  else None )

我们可以通过移除地图再走一步。在这种情况下,保持包含键的元组是有帮助的,因为它不需要获取列表的第一项:

代码语言:javascript
运行
复制
let getDuplicates = numbers |> List.groupBy id
                            |> List.choose( fun( key, set ) ->
                               if set.Length > 1
                                  then Some key
                                  else None )

这比原来的更简单吗?也许吧。因为选择结合了两个目的,因此它必然比分开的目的( filtermap)更加复杂,这使得人们很难一目了然地理解它,也许会取消更“简化”的代码。稍后再谈这个。

分解概念

不过,简化代码并不是直接的问题。您询问了在查找重复项时有用的函数。在高层次上,你如何找到一个副本?这取决于您的算法和特定需求:

  • 您的给定算法使用“根据其值将项放入桶中”和“查找具有多个项的桶”。这是与List.groupByList.choose (或filter/map)的直接匹配。
  • 一种不同的算法可以是“遍历所有项”,“修改累加器,就像我们看到每个项一样”,然后“报告已经多次看到的所有项”。这有点像第一种算法,比如List.fold正在代替List.groupBy,但是如果您需要拖动其他类型的状态,这可能会有所帮助。
  • 也许你需要知道有多少次重复。满足这些要求的另一种算法可能是“对项进行排序,以便它们始终是升序的”,以及“如果下一项与当前项相同,则标记”。在本例中,您有一个List.sort和一个List.toSeq,然后是Seq.windowed: 设getDuplicates =数字|> List.sort |> List.toSeq |> Seq.windowed 2 |> Seq.choose(当x=y -> (x=y -> )(x=y->,x_x_->_ None时) 请注意,这将返回一个5;9;9的序列,通知您9被重复了两次。
  • 这些算法主要是基于列表函数的。已经有两个答案,一个是可变的,另一个不是,这是基于集合和存在的。

我的观点是,一份完整的函数列表有助于查找副本,它读起来就像现有集合函数的“谁是谁”--这完全取决于您要做的事情和您的具体需求。我认为您对List.groupBy和List.choose的选择可能非常简单。

可维护性简化

关于简化的最后一个想法是记住,简化代码会在一定程度上提高代码的可读性。超出这一点的“简化”很可能涉及到诡计,或模糊的意图。如果我回顾几周前编写的代码示例和几个项目,那么最短、也许最简单的代码可能不是最容易理解的。因此,最后一点--简化未来的代码可维护性--可能是您的目标。如果是这样的话,您的原始算法只修改了groupBy元组,并对管道的每一步所做的工作添加了注释,这可能是您的最佳选择:

代码语言:javascript
运行
复制
// combine numbers into common buckets specified by the number itself
let getDuplicates = numbers |> List.groupBy id
                            // only look at buckets with more than one item
                            |> List.filter( fun (_,set) -> set.Length > 1)
                            // change each bucket to only its key
                            |> List.map( fun (key,_) -> key )

最初的问题评论已经表明,对于不熟悉代码的人来说,您的代码是不清楚的。这是一个经验问题吗?一定。但是,无论我们是在一个团队中工作,还是孤军奋战,优化代码(在可能的情况下)快速理解应该是每个人的首要任务。(从沙箱上爬下来) :)

不管怎样祝你好运。

票数 14
EN

Stack Overflow用户

发布于 2016-04-06 16:23:42

如果您不介意在本地范围内使用可变集合,则可以这样做:

代码语言:javascript
运行
复制
open System.Collections.Generic

let getDuplicates numbers =
    let known = HashSet()
    numbers |> List.filter (known.Add >> not) |> set
票数 9
EN

Stack Overflow用户

发布于 2016-04-06 16:00:40

您可以将最后三个操作包装在一个List.choose

代码语言:javascript
运行
复制
let duplicates =
   numbers 
   |> List.groupBy id
   |> List.choose ( function
          | _, x::_::_ -> Some x
          | _ -> None )
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36455768

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档