【问题标题】:Mapping over sublists in Scala在 Scala 中映射子列表
【发布时间】:2010-10-27 20:29:29
【问题描述】:

我知道 map 函数获取列表(序列)的每个元素并对其应用函数。递归(并且不考虑终止条件等)

map(s, f) = f(s.head) :: map(s.tail, f)

我正在寻找类似的功能

foo(s, f) = f(s) :: map(s.tail, f).

所以是一个“映射器”,其中映射函数在子列表而不是单个元素上调用。在 lisp 术语中,我正在寻找一个地图列表,而不是地图车。这样的事情是否存在,还是我必须自己滚动(或使用递归)?

或者,我会采用一个函数,该函数将一个序列作为输入并返回一个中端子序列序列,即

bar(s, f) = s :: bar(s.tail, f)

【问题讨论】:

  • 你能举一个所需输入和输出的例子吗?我跟不上。
  • 我不知道这个问题的答案,但是如果您需要高级类型和函数,请参阅 Scalaz 库:code.google.com/p/scalaz

标签: scala functional-programming list-manipulation


【解决方案1】:

/* 这种方法用另一种称为tails 的有用方法来定义mapList。和 Daniel 一样,我会将它放在 List 的隐式扩展中,但这纯粹是个人喜好问题 */

implicit def richerList[A](list : List[A]) = new {

/* 这是一个名为 tails 的方法,它返回列表中每个可能的尾部。它是尾递归的,因此它不会在大型列表中爆炸。请注意,它与同名的 Haskell 函数略有不同。 Haskell 版本总是在结果中添加一个空列表 */

  def tails : List[List[A]] = {
    def loop(ls : List[A], accum : List[List[A]]) : List[List[A]] = ls match {
      case _ :: tail => loop(tail, ls :: accum)
      case _ => accum
    }

    loop(list, Nil).reverse
  }

/* 这就是使用尾巴的样子

scala> "abc".toList.tails
res0: List[List[Char]] = List(List(a, b, c), List(b, c), List(c))

*/

/* 现在我们可以根据tails定义mapList */

  def mapList[B](f : List[A] => B) = tails map f
}

/* 这就是使用 mapList 的样子

scala> "abc".toList mapList (_.reverse.mkString)
res1: List[String] = List(cba, cb, c)

*/

【讨论】:

    【解决方案2】:

    另一个答案很接近,但除非绝对必要,否则您应该永远使用List#length。特别是,当问题本身为O(n)时,他的解O(n^2)。这是一个清理后的版本:

    implicit def addListSyntax[A](list: List[A]) = new {
      def mapList[B](f: List[A]=>B) = {
        // use inner function to avoid repeated conversions
        def loop(list: List[A]): List[B] = list match {
          case ls @ (_ :: tail) => f(ls) :: loop(tail)
          case Nil => Nil
        }
    
        loop(list)
      }
    }
    

    而且,要回答您最初的问题:不,没有办法使用标准实用程序方法来做到这一点。我其实有点好奇你为什么想要这样的东西……

    【讨论】:

    • 这里要小心,因为这个版本不是尾递归的。大列表会导致堆栈爆炸。
    • 确实,这是一个巨大的错误。
    • 我有一个排序的时间戳列表,我需要映射诸如 - 每个时间戳和之前的 y 个时间戳 - 每个时间戳,以及它之前 x 分钟的所有时间戳。同样,我确切地知道如何以多种方式对其进行编码(通过命令式地定义我自己的 mapList,使用 map 然后在内部过滤以生成历史记录等),但我想了解如何干净地做到这一点以及 Scala 方式。
    【解决方案3】:

    您基本上已经在伪代码中定义了您要查找的内容 - 因此使用隐式转换将此类方法添加到 Scala 列表很容易:

    object ExtendedList{
      implicit def List2ExtendedList[A](l:List[A])=new ExtendedList(l)
    }
    class ExtendedList[A](l:List[A]){
      import ExtendedList._
      def mapList[B](f:List[A]=>B):List[B]=l.length match {
        case 0 => List()
        case _ => f(l)::l.tail.mapList(f)
      }
    }
    
    object Test extends Application{
      import ExtendedList._
      val test = List(5,4,3,2,1)
      assert(List(15,10,6,3,1)==test.mapList{l=>(0/:l){_+_}})
    }
    

    这就是你要找的吗?

    【讨论】:

    • 获取列表的长度需要 O(n),而您的 mapList 会执行 n 次,成本为 O(n^2)。在一个大列表上运行它,你会等待一段时间:-)
    • 我问的原因是我是 Scala 新手,想知道是否有内置的规范函数(以使我的代码对其他人更具可读性)。或者,我想听听“不要那样做,scala 的做法是___”
    • 是的,我承认使用 .length 不好 - 应该多考虑一下!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-27
    • 2011-10-23
    • 1970-01-01
    相关资源
    最近更新 更多