我正在做一个关于scala流的练习。我从书中得到了以下代码。(我写了toList函数)
trait Stream2[+A] {
def uncons: Option[(A, Stream2[A])]
def isEmpty: Boolean = uncons.isEmpty
def toList: List[A] = {
def toListAcc(l:List[A], s:Stream2[A]):List[A]= {
s.uncons match {
case Some((h, t)) => toListAcc(List(h) ++ l, t)
case None => l
}
}
toListAcc(List(), this).reverse
}
def take(n: Int): Stream[A] = {
???}
}
object Stream2 {
def empty[A]: Stream2[A] =
new Stream2[A] { def uncons = None }
def cons[A](hd: => A, tl: => Stream2[A]): Stream2[A] =
new Stream2[A] {
lazy val uncons = Some((hd, tl))
}
def apply[A](as: A*): Stream2[A] =
if (as.isEmpty) empty
else cons(as.head, apply(as.tail: _*))
}
val s = Stream2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) //> s : chapter5.Stream2[Int] = chapter5$$anonfun$main$1$Stream2$3$$anon$2@782a
//| fee5
s.toList //> res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
}
现在,我必须编写take函数,该函数创建一个只有前n个元素的流。我完全迷失了方向,不知道该怎么办。我认为我的问题是因为我没有完全理解Stream2对象是如何创建的。我想这与uncons
函数(或者可能是cons
)有关。
所以最后我想:
1) 解释这个代码应该如何工作
2) 要知道toList是否在延迟评估拨号方式方面写得正确
3) 该函数的代码需要或至少需要一些提示才能自己编写。
一个可能的解决方案,实现take和其他一些函数。代码仍然很粗鲁,因此更好的答案是欢迎
object chapter5 {
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
trait Stream2[+A] {
def uncons: Option[(A, Stream2[A])]
def isEmpty: Boolean = uncons.isEmpty
def toList: List[A] = {
def toListAcc(l: List[A], s: Stream2[A]): List[A] = {
s.uncons match {
case Some((h, t)) => toListAcc(List(h) ++ l, t)
case None => l
}
}
toListAcc(List(), this).reverse
}
def take(n: Int): Stream2[A] = {
def takeAcc(n: Int, s: Stream2[A], acc: Stream2[A]): Stream2[A] =
{
if (n == 0) acc
else {
s.uncons match {
case Some((h, t)) => takeAcc(n - 1, t, Stream2.cons(h, acc))
case None => acc
}
}
}
val sReverse = takeAcc(n, this, Stream2())
takeAcc(n, sReverse, Stream2())
}
def foldRight[B](z: => B)(f: (A, => B) => B): B =
uncons match {
case Some((h, t)) => f(h, t.foldRight(z)(f))
case None => z
}
def exists(p: A => Boolean): Boolean =
foldRight(false)((a, b) => p(a) || b)
def map[B](f: A => B): Stream2[B] = {
foldRight(Stream2[B]())((x, y) => Stream2.cons(f(x), y))
}
def forAll(p: A => Boolean): Boolean = {
foldRight(true)((x, y) => p(x) && y)
}
}
// end of trait
object Stream2 {
def empty[A]: Stream2[A] =
new Stream2[A] { def uncons = None }
def cons[A](hd: => A, tl: => Stream2[A]): Stream2[A] =
new Stream2[A] {
lazy val uncons = Some((hd, tl))
}
def apply[A](as: A*): Stream2[A] =
if (as.isEmpty) empty
else cons(as.head, apply(as.tail: _*))
}
//end of obj Stream2
val s = Stream2(1, 2, 3, 4) //> s : chapter5.Stream2[Int] = chapter5$$anonfun$main$1$Stream2$3$$anon$2@70c
//| 73be3
s.toList //> res0: List[Int] = List(1, 2, 3, 4)
Stream2.cons(0, s).toList //> res1: List[Int] = List(0, 1, 2, 3, 4)
s.take(5).toList //> res2: List[Int] = List(1, 2, 3, 4)
s.foldRight(0)((x, y) => x + y) //> res3: Int = 10
s.map(x=>x*x).toList //> res4: List[Int] = List(1, 4, 9, 16)
s.forAll(x=>x%2==0) //> res5: Boolean = false
s.forAll(x=> x>0) //> res6: Boolean = true
}