我正在研究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帖子,但不同的类型对我不起作用):
答案 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,因此您可以在迭代列表时看到累加器。