【问题标题】:Scala partition into more than two listsScala 划分为两个以上的列表
【发布时间】:2014-10-31 12:54:45
【问题描述】:

我在 Scala 中有一个列表,我试图根据涉及列表多个元素的谓词将其划分为多个列表。例如,如果我有

a: List[String] = List("a", "ab", "b", "abc", "c")

我想得到b: List[List[String]],它是List[String] 的列表,这样内部List[String] == 3 的长度之和。即List(List("a", "b", "c"), List("abc"), List("ab", "a"), ...)

[编辑] 长度为 50 或更少的列表需要花费合理的时间。

【问题讨论】:

    标签: list scala


    【解决方案1】:

    对于任意谓词p,不可能构建比O(2^n * O(p)) 更便宜的有效算法。这是因为必须评估每个子集。你永远不会实现对n == 50 有用的东西。

    【讨论】:

      【解决方案2】:

      构建所有可能的子列表和过滤器:

      def filter[A](list: List[A])(predicate: (List[A] => Boolean)): List[List[A]] = {
        (for {i <- 1 to list.length
              subList <- list.combinations(i)
              if predicate(subList)
        } yield subList).toList
      }
      
      val a = List("a", "ab", "b", "abc", "c")
      
      val result = filter(a)(_.foldLeft(0)(_ + _.length) == 3)
      

      【讨论】:

      • 对不起,这对我的目的来说效率太低了,它变得非常慢。
      • 嗯。您没有定义任何性能要求。所以我真的不明白你的反对意见。
      • 对不起 :) 所以更新你的帖子,用性能要求或其他。
      【解决方案3】:

      我认为 Sergey 在这方面进展顺利,但我们可以稍微优化他的代码。首先,我们可以注意到,如果字符串长度的总和为 N,那么我们肯定不需要检查由 N 个以上的字符串组成的组合,因为最短的字符串至少有一个字符长。而且,此外,我们可以不用 for 合体糖,而是使用 sum 方法而不是更通用的方法(因此,可能不会那么快)向左折叠

      为了清楚起见,让我们首先定义一个小的帮助函数,它将计算字符串长度的总和:

      def sumOfStr(list: List[String]) = list.map(_.length).sum
      

      现在是主要方法:

      def split(list: List[String], sum: Int) = 
        (1 to sum).map(list.combinations(_).filter(sumOfStr(_) == sum)).flatten.toList
      

      编辑:结合我们的力量,我们为您提供了一个仍然非常低效的版本,但是嘿,这是我们可以在合理时间内做的最好的版本:

      def sumOfStr(lst: List[String]) = {
        var sum = 0
        lst.foreach{ sum += _.length }
        sum  
      }
      
      def split(lst: List[String], sum: Int) = 
        (1 to sum).par
        .map(lst.combinations(_).filter(sumOfStr(_) == sum))
        .flatten.toList
      

      【讨论】:

      • list.view.map(_.length).sum 会更快。 .map 会导致构建一个新的列表,该列表会立即被丢弃。
      • @nate,可能,但需要测量。 .view 也有开销。
      • 这是一个快速而肮脏的测试,显示了时间差异。似乎时间有所不同,但平均而言.view 比没有时间要快一些。 .iterator.view 稍快。展开它总是更快。 gist.github.com/nbauernfeind/43f3ac146368d166531d
      • 我修改了要点以求和字符串的长度(这是一个常数时间操作,而不是 list.length,即 O(n))。出于某种原因,.iterator 始终比.view 慢。
      • 谢谢,Nate :) 实际上,在我的电脑上,viewiterator 快得多:3.0s 到 3.8s。无论如何unrolled 胜过一切。
      猜你喜欢
      • 2013-08-09
      • 2014-08-21
      • 2016-07-30
      • 2023-01-27
      • 2023-03-19
      • 2020-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多