Scala—匿名函数的递归
我正在研究scala实验室的东西,我正在构建一个函数,最终将返回如下内容:Scala—匿名函数的递归,scala,recursion,anonymous-function,Scala,Recursion,Anonymous Function,我正在研究scala实验室的东西,我正在构建一个函数,最终将返回如下内容: 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)) els
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);
}
这一切都很好,但是我需要两个函数来完成这项工作,这让我感到很烦恼。我尝试为匿名函数切换:trailtil(List()::List(l))
,但出现了以下错误类型不匹配;从IDE中找到: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))
请有人告诉我我做错了什么,或者用一种更好的方法来做这件事
(我确实看了这篇文章,但不同的类型对我来说并不合适):这个怎么样:
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)
顺便说一句,尽量避免使用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
}
实际上,您可以在另一个
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
(请参阅):
tails
返回Iterator
,所以如果需要,您需要将其转换为list(就像我所做的那样)。此外,结果最后还将包含一个额外的空值(在我的示例中,结果将是列表(列表(1,2,3,4),列表(2,3,4),列表(3,4),列表(4),列表())
),因此您需要处理它。您所做的错误是:
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
在其定义中是指自身。要解决这个问题,您可以用一个惰性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
放在另一个中。您也可以使用折叠:
val l = List(1,2,3,4)
l.foldLeft(List[List[Int]](l))( (outerList,element) => {
println(outerList)
outerList.head.tail :: outerList
})
第一个参数列表是起始值/累加器。第二个函数是修饰符。通常,它修改起始值,然后将其传递给列表中的每个元素。我加入了一个println,这样你可以在列表迭代时看到累加器。这很好,但我只了解其中的一半。我不理解的主要问题是x
从何而来。谢谢你的长度信息@洛里扎克:嗯,我也不明白x
是从哪里来的,我的解决方案中没有x
;-)。试着用纸和笔浏览一下这段代码,你会看到递归在这里是如何工作的。WTF!我想我看到了一些东西!在情况h
中,h
来自何处?h是一个变量,其值来自被匹配的对象。它只是指尾巴。如果您愿意,可以在case语句中使用它。例如,您可以说:case first::second::third::Nil=>println(first+“”+second+“”+third)。这将匹配任何具有三个元素的列表,并将这些元素分配给变量first、second和third。用法:scala>List(1,2,3)match{case first::second::third::Nil=>println(first+“”+second+“”+third)case}>println(“No match”)}1,2,3列表(1,2,3,4)打印“No match”,这是模式匹配的一部分。这是一个非常广泛的话题,请参见。从Prolog到Haskell再到Clojure,您可以在多种语言中找到相同的结构。虽然这是真的,但它与我的结构没有太大的不同。@locrizak:我不认为您可以使用一级函数进行递归,为此您需要def
。Ballin!你知道使用foldLeft比其他例子有没有额外的开销吗?我不这么认为。看一看源代码:106 override/*TraversableLike*/107 def foldLeft[B](z:B)(f:(B,a)=>B):B={108 var acc=z 109 var this=this 110而(!this.isEmpty){111 acc=f(acc,this.head)112 this=this.tail 113}114 acc 115}哇,没有干净地发布。无论如何,fold left/:我总是忘记它,只是键入foldLeftA。许多scala集合库的编写看起来像是递归的,但实际上由于jvm和堆栈大小的限制,它们是以迭代方式编写的。这就是一个例子。这里有一个很好的解释他们试图实现的目标:(也摘自我极力推荐的《Scala编程》一书)。我想自己尝试一些锻炼,所以我找到了scala实验室。现在每当有一个我不懂的练习时,我都会蹦蹦跳跳,你的问题的标题包括“匿名”。这些不是匿名函数,其形式为(inputs)=>表达式
,即没有名称。我不知道在Scala中,是否可以递归一个没有名字的函数。在Javascript中,可以使用不推荐的。
val l = List(1,2,3,4)
l.foldLeft(List[List[Int]](l))( (outerList,element) => {
println(outerList)
outerList.head.tail :: outerList
})