【问题标题】:Scala filter on a list by indexScala按索引过滤列表
【发布时间】:2013-09-19 19:21:10
【问题描述】:

我想把它写成函数式的,我能做的最好的就是:

list.zipWithIndex.filter((tt:Tuple2[Thing,Int])=>(tt._2%3==0)).unzip._1

获取元素 0, 3, 6,...

有没有更易读的 Scala 习语?

【问题讨论】:

  • 如何定义功能性的?对于理解是否可以接受?流?迭代器?还是您的意思是 List 组合函数?
  • 我只是想排除程序性答案。我主要关心的是可读性和简洁性。
  • 然后我猜 om-nom-nom 的索引方法和我的回答中第一个天真的理解力都符合条件。
  • 我同意。在我正式接受之前,需要有机会和他们一起玩代码。

标签: scala functional-programming scala-collections


【解决方案1】:

不太清楚,但仍然:

xs.indices.collect { case i if i % 3 == 0 => xs(i) }

【讨论】:

  • 您可能要添加索引访问应该是O(1),否则这将不会执行O(n)
【解决方案2】:

Clojure 有一个 take-nth 函数可以满足您的需求,但我惊讶地发现 Scala 中没有等效的方法。您可以根据 Clojure 代码编写类似的递归解决方案,或者您可以阅读这篇博文:

Scala collections: Filtering each n-th element

作者实际上在最后有一个很好的图表,显示了他的每个解决方案的相对性能。

【讨论】:

    【解决方案3】:

    如果效率不是问题,您可以执行以下操作:

    list.grouped(3).map(_.head)
    

    请注意,这会构建中间列表。

    您也可以使用理解:

    for {
      (x,i) <- list zipWithIndex
      if i % 3 == 0
    } yield x
    

    这当然与您原来的解决方案几乎相同,只是写法不同。

    我的最后一个选择是在压缩列表中使用 collect:

    list.zipWithIndex.collect {
      case (x,i) if i % 3 == 0 => x
    }
    

    【讨论】:

      【解决方案4】:

      我会像在 Octave 数学程序中那样做。

      val indices = 0 until n by 3  // Range 0,3,6,9 ...
      

      然后我需要一些方法来从集合中选择索引。显然我必须有一个随机访问 O(1) 的集合。喜欢ArrayVector。例如这里我使用Vector。要将访问包装到一个不错的 DSL 中,我会添加一个隐式类:

      implicit class VectorEnrichedWithIndices[T](v:Vector[T]) {
        def apply(indices:TraversableOnce[Int]):Vector[T] = {
          // some implementation 
          indices.toVector.map(v)
        }
      }
      

      用法如下:

      val vector = list.toVector
      val every3rdElement = vector(0 until vector.size by 3)
      

      【讨论】:

        【解决方案5】:

        啊,这个怎么样?

        val l = List(10,9,8,7,6,5,4,3,2,1,0)
        for (i <- (0 to l.size - 1 by 3).toList) yield l(i)
        //res0: List[Int] = List(10, 7, 4, 1)
        

        这可以通过

        变得更通用
        def seqByN[A](xs: Seq[A], n: Int): Seq[A] = for (i <- 0 to xs.size - 1 by n) yield xs(i)
        
        scala> seqByN(List(10,9,8,7,6,5,4,3,2,1,0), 3)
        res1: Seq[Int] = Vector(10,7,4,1)
        
        scala> seqByN(List(10,9,8,7,6,5,4,3,2,1,0), 3).toList
        res2: Seq[Int] = List(10,7,4,1)
        
        scala> seqByN(List[Int](), 3)
        res1: Seq[Int] = Vector()
        

        但是 functional 你的意思是只使用各种 List 组合函数吗?否则,Streams 功能是否足够?

        def fromByN[A](xs: List[A], n: Int): Stream[A] = if (xs.isEmpty) Stream.empty else
          xs.head #:: fromByN(xs drop n, n)
        
        scala> fromByN(List(10,9,8,7,6,5,4,3,2,1,0), 3).toList
        res17: List[Int] = List(10, 7, 4, 1)
        

        【讨论】:

          【解决方案6】:

          一个不错的实用解决方案,无需创建临时向量、列表等:

          def everyNth[T](xs: List[T], n:Int): List[T] = xs match {
            case hd::tl => hd::everyNth(tl.drop(n-1), n)
            case Nil => Nil
          }
          

          【讨论】:

            猜你喜欢
            • 2012-08-04
            • 2018-10-29
            • 1970-01-01
            • 2021-02-09
            • 2015-11-26
            • 2021-07-23
            • 2022-06-20
            • 1970-01-01
            相关资源
            最近更新 更多