Scala - 匿名函数的递归

时间:2011-12-01 20:01:52

标签: scala recursion anonymous-function

我正在研究scala labs的东西,我正在构建一个函数,最终会返回这样的东西: tails(List(1,2,3,4)) = List(List(1,2,3,4), List(2,3,4), List(3,4), List(4), List())

我通过使用两个函数并在第二个函数上使用一些递归来实现这一点。

def tails[T](l: List[T]): List[List[T]] = {
    if ( l.length > 1 )trailUtil(List() ::: List(l))
    else List() ::: List(l);
}

def trailUtil[T](l:List[List[T]]) : List[List[T]] = {
    if ( l.last.length == 0)l
    else trailUtil(l :+ l.last.init);
}

这一切都很棒,但是我需要两个功能来解决这个问题。我尝试切换:trailUtil(List() ::: List(l))以获取匿名函数,但我从IDE中收到此错误type mismatch; found :List[List[T]] required:Int

val ret : List[List[T]] = (ll:List[List[T]]) => {
    if ( ll.last.length == 0) ll else ret(ll :+ ll.last.init)
}
ret(List() ::: List(1))

有人可以指出我做错了什么,或者更好的做法,这会很棒。

(我确实看过this SO帖子,但不同的类型对我不起作用):

4 个答案:

答案 0 :(得分:4)

这个怎么样:

def tails[T](l: List[T]): List[List[T]] = 
  l match {
    case h :: tail => l :: tails(tail)
    case Nil => List(Nil)
  }

稍微不那么惯用的版本:

def tails[T](input: List[T]): List[List[T]] =
    if(input.isEmpty)
        List(List())
    else
        input :: tails(input.tail)

BTW尽量避免List.length,它会在O(n)时间内运行。

更新:正如 tenshi 所建议的,尾递归解决方案:

@tailrec def tails[T](l: List[T], init: List[List[T]] = Nil): List[List[T]] =
    l match {
        case h :: tail => tails(tail, l :: init)
        case Nil => init
    }

答案 1 :(得分:2)

您实际上可以在另一个def内定义def。它允许定义实际具有名称的函数,该函数可以被引用并用于递归。以下是tails的实施方式:

def tails[T](l: List[T]) = {
    @annotation.tailrec
    def ret(ll: List[List[T]]): List[List[T]] =
        if (ll.last.isEmpty) ll 
        else ret(ll :+ ll.last.tail)

    ret(l :: Nil)
}

此实现也是尾递归的。我添加了@annotation.tailrec注释以确保它确实存在(如果不是,代码将无法编译)。


您还可以使用内置函数tails(请参阅ScalaDoc):

List(1,2,3,4).tails.toList

tails会返回Iterator,因此如果需要,您需要将其转换为列表(就像我一样)。最后结果将包含一个额外的空(在我的示例中结果将是List(List(1, 2, 3, 4), List(2, 3, 4), List(3, 4), List(4), List())),因此您需要处理它。

答案 2 :(得分:1)

你做错了是这样的:

val ret : List[List[T]]

所以ret是T列表。然后你这样做:

ret(ll :+ ll.last.init)

这意味着您在T列表的列表中调用方法apply。列表的apply方法采用Int参数,并返回带有该索引的元素。例如:

scala> List("first", "second", "third")(2)
res0: java.lang.String = third

我假设你想要来编写val ret: List[List[T]] => List[List[T]],即一个带List[List[T]]并返回List[List[T]]的函数。那么你还有其他问题,因为val在其定义中指的是它自己。要解决这个问题,您可以将其替换为lazy val

def tails[T](l: List[T]): List[List[T]] = {
  lazy val ret : List[List[T]] => List[List[T]] = { (ll:List[List[T]]) => 
    if ( ll.last.length == 0) ll 
    else ret(ll :+ ll.last.init)
  }
  if ( l.length > 1 )ret(List() ::: List(l))
  else List() ::: List(l);
}

但是,当然,简单的解决方案是将一个def置于另一个内,如tenshi建议的那样。

答案 3 :(得分:1)

你也可以使用折叠:

val l = List(1,2,3,4)

l.foldLeft(List[List[Int]](l))(  (outerList,element) => {
    println(outerList)
    outerList.head.tail :: outerList
})

第一个参数列表是您的起始值/累加器。第二个功能是修饰符。通常,它会修改起始值,然后将其传递给列表中的每个元素。我包含了一个println,因此您可以在迭代列表时看到累加器。