我发现自己在大多数功能结束时都会反转累加器;我该怎么办?

时间:2016-02-09 17:04:29

标签: scala functional-programming

我一直在写一本关于Scala中的函数式编程的书(有这个标题。)通过练习,我发现自己经常在用累加器收集时反转我的最终结果。我记得在我的球拍日期间有类似的模式。我担心的是,我可能会使我的代码比必要的稍微混乱,并且可能执行额外的O(n)操作(其中n是累加器/结果的长度。)

示例:

// Produces a List from a Stream, forcing evaluation of all elements.
def toList(): List[A] = {
    def go(l: Stream[A], acc: List[A]): List[A] = {
        l match {
            case Empty if acc.nonEmpty => acc.reverse
            case Empty if acc.isEmpty => Nil
            case Cons(h, t) => go(t(), h() :: acc)
        }
    }
    // "this" is a Stream[A].
    go(this, Nil)
}

这种扭转结果以恢复原始顺序的模式是我关心的问题。是否有更好的方法(没有reverse调用)在FP中,特别是在Scala中执行此操作?

4 个答案:

答案 0 :(得分:5)

您可以尝试使用Vector等数据结构,它将在有效的恒定时间附加值。

所以你在哪里:

case Cons(h, t) => go(t(), h() :: acc)

改为使用:

case Cons(h,t) => go(t(), acc :+ h())

当你返回累加器时,不需要反转。

答案 1 :(得分:3)

嗯,有各种各样的方法,即使没有转向非尾部,也不应该超过可能大的容器的大小。

首先你应该问:你为什么关心?如果您的代码足够快,为什么要避免反转?

然后最简单的解决方案可能是:在本地使用可变构建器。有一个ListBuffer,用于通过逐步追加元素来构建列表。由于性能原因,Scala API中的实现会大量使用它。

如果您不想直接使用列表缓冲区,则可以进行延迟计算并将其转换为列表。在某些情况下,这可能比使用累加器更具可读性,但它可能不会更有效。

    > Sample.Frame
                              Start.Date LON.NEW6 LON.CAP LON.CEN2 LON.MYH1 LON.SOS LON.MOW LON.HOLW LON.94V LON.FOU6 LON.949
E0-001-085571068-9            30/09/2015       3       NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-086838711-7            07/11/2015       1       NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-085536178-4@2015102019 20/10/2015       NA      NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-085466318-0            01/07/2016       NA      NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-085591039-5            30/01/2016       NA      NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-087500856-4            29/04/2016       NA      NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-079398784-2@2015092909 29/09/2015       NA      NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-086021337-5            14/10/2015       NA      NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-086639435-3            20/12/2015       NA      NA       NA       NA      NA      NA       NA      NA       NA      NA
E0-001-087220018-9            27/10/2015       NA      NA       NA       NA      NA      NA       NA      NA       NA      NA

答案 2 :(得分:3)

如果你想保持尾递归,你应该使用Difference List作为累加器:它们基本上是一个带有指向Nil构造函数的直接指针的列表,它允许你在常量中进行连接时间。

答案 3 :(得分:-4)

根本不使用累加器

def toList(): List[A] = {
    def go(l: Stream[A]): List[A] = {
        l match {
            case Empty  => Nil
            case Cons(h, t) => h :: go(t)
        }
    }
    go(this)
}

如评论中所述,此解决方案不再是尾递归。是什么累积器主要用于。