Skip to content

Instantly share code, notes, and snippets.

@samouss
Created August 24, 2017 21:22
Show Gist options
  • Save samouss/1427a43d0e3ea4be207322724e658f37 to your computer and use it in GitHub Desktop.
Save samouss/1427a43d0e3ea4be207322724e658f37 to your computer and use it in GitHub Desktop.
Scala Stream
class e1_bonus_stream extends HandsOnSuite {
sealed trait Stream[+A] {
def map[B](fonction:A => B):Stream[B]
def flatMap[B](fonction:A => Stream[B]):Stream[B]
def filter(fonction:A => Boolean):Stream[A]
final def union[B >: A](stream: => Stream[B]):Stream[B]= {
this match {
case EmptyStream => stream
case Cons(head, tail) => Cons(head, union(tail))
}
}
def isEmpty:Boolean
def take(n: Int): Stream[A] = {
this match {
case EmptyStream => EmptyStream
case cons: Cons[A] => {
if (n <= 0) EmptyStream
else if (n == 1) Cons(cons.head, EmptyStream)
else Cons(cons.head, cons.tail.take(n - 1))
}
}
}
def foreach(effetDeBord:A => Unit):Unit
}
object Stream {
// 'A*' signifie var args de A
def apply[A](values:A*):Stream[A] = {
if (values.isEmpty) {
EmptyStream
} else {
// ':_*' permet d'étendre une Stream en var args
Cons(values.head, Stream(values.tail:_*))
}
}
def unapply[A](xs: Stream[A]): Option[(A, Stream[A])] = {
xs match {
case EmptyStream => None
case cons:Cons[A] => Some((cons.head, cons.tail))
}
}
}
/**
* Cons veut dire Constructor, c'est lui qui permet de construire la Stream en ajoutant un élément
* à la queue
*/
final class Cons[A](val head:A, tl: => Stream[A]) extends Stream[A] {
def isEmpty = false
// ce mécanisme permet de garantir la lazyness de la queue de la stream
// ainsi que la mémoization des valeurs accédées
lazy val tail:Stream[A] = tl
def map[B](fonction:A => B):Stream[B] = Cons(fonction(head), tail.map(fonction))
/**
* l'implémentation de flatMap a besoin d'union
*/
def flatMap[B](fonction:A => Stream[B]):Stream[B] = {
fonction(head).union(tail.flatMap(fonction))
}
override def filter(fonction:A => Boolean):Stream[A] = {
if (fonction(head)) {
Cons(head, tail.filter(fonction))
} else {
tail.filter(fonction)
}
}
override def equals(that: Any):Boolean = this == that
override def hashCode():Int = head.hashCode()
override def foreach(effetDeBord:A => Unit):Unit = {
effetDeBord(head)
tail.foreach(effetDeBord)
}
override def toString: String = "Cons(" + head + "," + tail.toString + ")"
}
object Cons {
def apply[A](head:A, tl: => Stream[A]) = new Cons(head,tl)
def unapply[A](cons:Cons[A]) = Stream.unapply(cons)
}
/**
* il y a qu'un seul EmptyStream, donc cela peut être un case object
*/
case object EmptyStream extends Stream[Nothing] {
type A = Nothing
def map[B](fonction:A => B):Stream[B] = EmptyStream
def flatMap[B](fonction:A => Stream[B]):Stream[B] = EmptyStream
def filter(fonction:A => Boolean):Stream[A] = EmptyStream
def isEmpty: Boolean = true
def foreach(effetDeBord:A => Unit):Unit = {}
override def toString: String = "EmptyStream"
}
exercice("lazyness 2") {
val s:Stream[Int] = Stream(1,2,3).map{
case 1 => 1
case _ => {
throw new Exception("I should be lazy")
}
}
// Dont throw
println(s.flatMap(x => Stream(x)).take(1).toString)
// Throw
println(s.flatMap(x => s.map(y => x + y)).take(1).toString)
val s2 = for (i <- s; j <- s) yield(i + j)
// Throw
s2.take(1).foreach(println)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment