如何在Scala中为列表实现自定义尾部递归映射

时间:2019-04-24 20:21:48

标签: scala

我想实现一个称为map的函数,该函数接收一个列表和一个函数,并产生将函数应用于输入列表的每个元素的结果。 到目前为止,我已经知道了:

        {
          "id": 129748,
          "ns": "av",
          "name": "Polarized"
        },
        {
          "id": 129898,
          "ns": "av",
          "name": "False"
        }

此代码可以正常工作,并且可以为我带来预期的结果,但是我不禁想到,有一种更优雅,更有效的方法不涉及使用def map(list: List[Int], function: (Int) => Int): List[Int] = { def loop(list: List[Int], acc: List[Int]): List[Int] = { list match { case Nil => acc case head :: tail => loop(list.tail, function(head) :: acc) } } loop(list.reverse, Nil) } 或其他非reverse的列表方法或head

2 个答案:

答案 0 :(得分:4)

只要滚动自己的纯功能 map函数实现,而无需使用任何可变状态或内置的List 高阶函数< / em>,您做得很棒!不必reverse列表似乎没有必要,但这是值得的,因为在列表之前添加前缀是非常有效的操作。

以及注释中建议的其他方法,您可以将acc设为scala.collection.mutable.ListBuffer(它支持有效的追加和前置操作),然后在完成后将其转换为列表。但是,转换过程与reverse相比并没有很大的进步。

否则,您可以做一些次要的事情来改善map功能:

  • 通过使用咖喱参数,您可以更优雅地使用map
  • 需要 tail-recursive 的功能应该始终用scala.annotation.tailrec属性修饰。这样, Scala 编译器就会知道您的意图,如果无法优化该函数以使用尾部递归,则会发出错误。
  • 使此函数通用是很简单的,这将使其用途更加广泛。
  • 通常会对结果执行reverse操作。它对 map 操作没有影响,但是例如,如果要编写 filter 操作,则反转可能较小的已过滤集合的效率更高。元素。

这是下面的样子:

import scala.annotation.tailrec

def map[A, B](list: List[A])(function: (A) => B): List[B] = {

  @tailrec
  def loop(rem: List[A], acc: List[B]): List[B] = rem match {
    case Nil => acc.reverse
    case head :: tail => loop(tail, function(head) :: acc)
  }
  loop(list, Nil)
}

答案 1 :(得分:3)

因为head :: tail是解构和重建List的最有效方法,所以您所做的是一个非常普通的模式。 .reverse很不幸,但通常会导致轻微的性能损失。

如评论中所述,map也可以通过其他标准库方法来实现。这是通过foldRight完成的:

def map(list :List[Int], function :Int => Int) :List[Int] = 
  list.foldRight(List.empty[Int])(function(_) :: _)