Streamの#::実装

5641 ワード

本文はfair-jmから来た.iteye.com転送先は出典を明記してください
 
フロントエンドの時間は本のFP in scala(電子版はpaypalが2元のクレジットカードを払うことができることができます)を買って太くstreamの不活性な価値を求めるあの章を見て自分で書きたいです
この本に沿って書きました.
そこでscalaクラスライブラリのscalaを考えた.collection.immutable.Streamの#::操作はどのように実現されますか:
 
	1 #:: {println(2);2} #:: {println("empty");scala.collection.immutable.Stream.empty[Int]}

#::不活性評価の場合、後の結果は評価されませんので、結果は
 
 
res0: scala.collection.immutable.Stream[Int] = Stream(1, ?)

出力2とemptyはありません
 
 
scalaの接尾辞式について私たちは接尾辞を実現することを知っています.では、この接尾辞演算子は必ず前または後のパラメータの1つの方法です.このオペレータは:終わりなので、彼はStreamの1つの方法です.私はこの考えに従って自分で先に試しました.
 
package cp5

sealed abstract class Stream[+A] {
    def take(n: Int): Stream[A]
    def drop(n: Int): Stream[A]
    def map[B >: A](f: A => B): Stream[B]
    
    def foreach[A](f: A => Unit): Unit = this match {
        case Empty =>
        case c: Cons[A] =>
            f(c.head)
            c.tail.foreach(f)
    }
    
    def #::[B >: A](e:B):Stream[B] = Stream.cons(e, this)
}

case object Empty extends Stream[Nothing] {
    override def take(n: Int): Stream[Nothing] = Empty
    override def drop(n: Int): Stream[Nothing] = Empty
    override def map[B](f: Nothing => B) = Empty
}

case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A] {
    lazy val head = h()
    lazy val tail = t()
    override def take(n: Int): Stream[A] = {
        if (n == 1) Cons(h, () => Empty)
        else Cons(h, () => tail.take(n - 1))
    }
    override def drop(n: Int): Stream[A] = {
        if (n == 1) tail
        else tail.drop(n - 1)
    }
    override def map[B >: A](f: A => B): Stream[B] = {
        Stream.cons(f(head), tail.map(f))
    }
}

object Stream {
    def cons[A, B >: A](h: => B, t: => Stream[A]): Stream[B] = {
        Cons(() => h, () => t)
    }

    def apply[A](e: A*): Stream[A] = {
        if (e.isEmpty) {
            Empty
        } else {
            Stream.cons(e.head, apply(e.tail: _*))
        }
    }
}

object Main {
    def main(args: Array[String]): Unit = {
        val s = Stream(1, 2, 3, 4, 5, 6).map((i: Int) => i * 2)
        1 #:: {println(2);2} #:: {println(4);Stream(3)}
    }
}

誤報はない
 
残念ながら運転中に印刷されます
 2
 3
後のパラメータに達していない不活性評価
 
後で考えてみたら間違っていた...Streamのメソッドであれば必ず{println(3);Stream(3)}に対してStream(3)}を求める(そうでなければどのように彼のメソッドを呼び出すか)
この#::Streamに書いてはいけないに違いない
でもどうやって実現するのか...しばらく考えてからソースを直接見たほうがいいと思いました.
案の定ソースコードの実現#::Streamの方法ではありません:
 
object Stream extends SeqFactory[Stream] {
 ... ...
  /** A wrapper class that adds `#::` for cons and `#:::` for concat as operations
   *  to streams.
   */
  class ConsWrapper[A](tl: => Stream[A]) {
    def #::(hd: A): Stream[A] = cons(hd, tl)
    def #:::(prefix: Stream[A]): Stream[A] = prefix append tl
  }
... ...
}

次の質問は、StreamがどうやってConsWrapperになったのかということです.
 
答えは明らかに暗黙的に変換されています
 
  implicit def consWrapper[A](stream: => Stream[A]): ConsWrapper[A] =
    new ConsWrapper[A](stream)

 
 
 
以下のように書きました.
 
package cp5

sealed abstract class Stream[+A] { ... }

case object Empty extends Stream[Nothing] {...}

case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A] {
    lazy val head = h()
    lazy val tail = t()
    override def take(n: Int): Stream[A] = {
        if (n == 1) Cons(h, () => Empty)
        else Cons(h, () => tail.take(n - 1))
    }
    ... ...
}

object Stream {
    def cons[A, B >: A](h: => B, t: => Stream[A]): Stream[B] = {
        Cons(() => h, () => t)
    }

    def apply[A](e: A*): Stream[A] = {
        if (e.isEmpty) {
            Empty
        } else {
            Stream.cons(e.head, apply(e.tail: _*))
        }
    }
    
  class ConsWrapper[A](tl: => Stream[A]) {
    def #::(hd: A): Stream[A] = cons(hd, tl)
  }
  
  implicit def convert[A](s: =>Stream[A]):ConsWrapper[A] = {
      new ConsWrapper(s)
  }
}

object Main {
    def main(args: Array[String]): Unit = {
        1 #:: {println(2);2} #:: {println(3);Stream(3)}
    }
}

次は運転が出力されていません2 3正常に動作します
 
 
また、ソースコードには、クラス:
 
  /** An extractor that allows to pattern match streams with `#::`.
   */
  object #:: {
    def unapply[A](xs: Stream[A]): Option[(A, Stream[A])] =
      if (xs.isEmpty) None
      else Some((xs.head, xs.tail))
  }

これについて洪江大のブログで言及されています.
scala霧の中の風景(5):接尾辞表現
 
ここでscalaはcall-by-nameの不活性評価をサポートし、暗黙的な変換はjavaでは実現できない多くの機能を実現することができるが、その実現方法は少し曲がりくねっている可能性がある.
まあscalaは本当に面白いと思いますが、今の仕事が使えないのはアマチュア趣味として少し残念です.