我试图通过实现我自己的懒惰列表来学习如何在Scala中使用内置的懒惰:
object LazyList {
def empty[A] : LazyList[A] = new LazyList[A] {
lazy val uncons = None
}
def cons[A](h : => A, t : => LazyList[A]) : LazyList[A] = new LazyList[A] {
lazy val uncons = Some( (h,t) )
}
def from(s : Int) : LazyList[Int] = new LazyList[Int] {
lazy val uncons = Some( (s,from(s + 1)) )
}
}
trait LazyList[A] {
import LazyList._
def uncons : Option[(A,LazyList[A])]
def fmap[B](f : A => B) : LazyList[B] = uncons match {
case None => empty
case Some( (h,t) ) => cons(f(h),t.fmap(f))
}
def take(i : Int) : LazyList[A] = uncons match {
case None => empty
case Some( (h,t) ) => if (i <= 0) empty else cons(h,t.take(i - 1))
}
override def toString : String = uncons match {
case None => "[]"
case Some( (h,t) ) => "[" ++ h.toString ++ ",..]"
}
}
这似乎有效,我可以,例如,将{ _ + 2}
映射到无限列表:
> LazyList from 1 fmap { _ + 2 }
res1: LazyList[Int] = [2,..]
我决定实现一些我经常使用的功能,例如drop
,take
等,除了inits
之外,我已经能够实现它们了。我对inits
的实施是:
def inits : LazyList[LazyList[A]] = uncons match {
case None => empty
case Some( (h,t) ) => cons(empty,t.inits.fmap(cons(h,_)))
}
问题是由于某种原因它无法在无限列表上工作。我不能,例如,写:
> LazyList from 1 inits
因为它永远运行。 fmap
之后问题似乎是t.inits
,由于某种原因,打破了懒惰(如果我删除fmap
它是错误的但是懒惰的)。为什么fmap
强制执行严格性,并且根据我的类型LazyList
,如何实施inits
以使其适用于无限列表?
答案 0 :(得分:8)
fmap
和inits
在调用时剥离一个实际(非惰性)元素;他们都uncons
。由于他们互相称呼,因此链永远不会终止于无限LazyList
。
具体来说,请注意uncons
不是=> LazyList
而是实际LazyList
,所以当你致电时
Some( (h,t) )
评估 t
。如果对t
的评估调用uncons
,它也会评估,并且您将进入堆栈溢出竞赛。
你需要让其中一个剥离零副本。你可以做到这一点的一种方法是使uncons
元组的第二个参数变为惰性(明确地,将其改为Function0
):
object LazyList {
def empty[A]: LazyList[A] = new LazyList[A] {
lazy val uncons = None
}
def cons[A](h: => A, t: => LazyList[A]) : LazyList[A] = new LazyList[A] {
lazy val uncons = Some( (h,() => t) )
}
def from(s: Int): LazyList[Int] = new LazyList[Int] {
lazy val uncons = Some( (s,() => from(s + 1)) )
}
}
trait LazyList[A] {
import LazyList._
def uncons: Option[(A,() => LazyList[A])]
def fmap[B](f: A => B): LazyList[B] = uncons match {
case None => empty
case Some( (h,t) ) => cons(f(h),t().fmap(f))
}
def take(i: Int): LazyList[A] = uncons match {
case None => empty
case Some( (h,t) ) => if (i <= 0) empty else cons(h,t().take(i - 1))
}
override def toString: String = uncons match {
case None => "[]"
case Some( (h,t) ) => "[" ++ h.toString ++ ",..]"
}
}
然后你的实施工作:
def inits: LazyList[LazyList[A]] = uncons match {
case None => empty
case Some( (h,t) ) => cons(empty,t().inits.fmap(cons(h,_)))
}
让一个内部的uncons做到这一点可能会更好,而一个外向的uncons会为你施加尾巴。