`

Stream的#::实现

 
阅读更多

本文来自fair-jm.iteye.com 转截请注明出处

 

前端时间买了本 FP in scala(电子版 可以paypal付 双币信用卡就可以) 粗粗地看 看到了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的中缀表达式 我们知道要实现中缀 那么这个中缀运算符一定是前面或者后面参数的一个方法 因为这个操作符是以 : 结尾的所以他就是Stream的一个方法 我按照这个思路自己先尝试了下:

 

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真的挺有趣的 但现在的工作用不上它 只能作为业余爱好略有可惜

 

0
0
分享到:
评论

相关推荐

    VideoStream说明1

    本文将深入探讨VideoStream的功能、工作流程及其核心函数,以帮助开发者理解如何在实际应用中实现视频流的播放和控制。 首先,VideoStream的核心功能是接收和处理来自手机App的VideoStream指令。当App发送"Video...

    探索Java 8的Stream API:数据流处理的新纪元

    4. **多线程**:Java内建了多线程支持,开发者可以轻松地实现并发编程,从而提高程序的执行效率。 5. **网络编程**:Java提供了一系列网络通信API,使得开发网络应用变得简单高效。 6. **简单性和动态性**:Java在...

    826 大数据StreamSets:一个大数据采集工具.docx

    StreamSets 的 DataOps 平台的真正价值在于业务用户和使用程序能够从各种数据源来访问原始数据和聚合数据,并且准时地产生数据驱动的生疏时,才能够实现。利用机器学习(Machine Learning),分析师和数据科学家...

    USB hub接四个camera.tar.gz

    用户下载后,应按照文档指示应用patch,更新内核或驱动,以及调整相关设置,以实现四个USB摄像头的稳定工作。 总之,通过理解错误原因、优化系统配置和更新驱动程序,我们可以克服"VIDIOC_STREAMON: No space left ...

    deepstream-test1-app_rtsp-master基于Deepstream实现RTSP视频流的读取

    【描述】中提到的"基于Deepstream实现RTSP视频流的读取"是指利用NVIDIA的Deepstream SDK,这是一个针对高性能视觉应用的开发工具包。Deepstream集成了GPU加速的计算机视觉、视频分析、以及流媒体功能,特别适合实时...

    Java 8 Stream API:数据流的函数式处理与高效编程

    Java 8的Stream API提供了一种强大且灵活的...通过使用Stream API,开发者可以轻松实现复杂的数据处理逻辑,同时保持代码的可读性和维护性。随着Java语言的不断发展,Stream API将继续在现代Java开发中发挥重要作用。

    async-stream:使用异步和等待符号的Rust异步流

    提供两个宏, stream! 和try_stream! ,允许调用方定义元素的异步流。 这些是使用async & await表示法实现的。 此板条箱可在没有不稳定特征的情况下工作。 stream! 宏返回实现特性的匿名类型。 与Item相关的类型...

    Java8 Stream学习

    - **基本类型的流**:对于int、long和double这些基本类型,Java8提供了专门的Stream实现:`IntStream`、`LongStream`和`DoubleStream`。 ```java IntStream intStream = IntStream.range(0, 10); // 生成0到9的...

    C++ 16进制和字符串批量转换

    在C++中,我们可以使用`std::stringstream`和`std::hex`来实现字符串到16进制的转换,以及`std::stringstream`和`std::dec`来实现16进制到字符串的转换。 1. **字符串到16进制转换:** 在C++中,可以逐个字符处理...

    微服务SpringBoot整合Redis基于Redis的Stream消息队列实现异步秒杀下单

    五、基于Redis Stream实现异步秒杀下单 在秒杀场景中,通过Redis Stream,可以将用户请求放入消息队列,后台服务异步处理下单逻辑,避免因高并发导致的系统性能瓶颈。生产者(如前端服务)将秒杀请求写入Stream,...

    stringstream的应用

    在上面的代码中,我们使用同一个 stringstream 对象实现了 string 到 int 的转换,然后又实现了 bool 到 int 的转换。我们需要在每一次转换之后调用 clear() 成员函数,以便重置 stream 对象。 stringstream 是 C++...

    std::string format格式化函数源代码及两种格式化方法

    对于更复杂的格式化需求,可以结合`std::stringstream`或者自定义函数来实现。 自定义`format`函数的优点是可以根据项目的特定需求进行定制,比如添加更多的格式化选项,或者提供更友好的API。这种方法在某些情况下...

    大多数程序员所接触到的套接字(Socket)为两类:

    ### 套接字(Socket)的两种主要类型 在计算机网络编程中,套接字...通过了解它们的特点和适用场景,开发人员可以更好地设计和实现网络应用程序。此外,原始套接字虽然不常见,但在特定的应用场景下也发挥着重要作用。

    C#中bitmap、stream、byte类型转换实例

    MemoryStream是一个实现了Stream接口的类,它可以直接访问其内部的字节数组。我们可以使用ToArray()方法将Stream中的数据转换为byte数组。 ```csharp byte[] imageBytes = memoryStream.ToArray(); // 将Stream转换...

    Stream、Lambda表达式练习.doc

    在实现中,我们首先将两个队伍的成员姓名存储到两个ArrayList集合中,然后使用Stream API对它们进行处理。我们使用filter方法对队伍进行过滤,然后使用limit或skip方法对结果进行限制或跳过。最后,我们使用concat...

    python实现简单的TCP代理服务器

    本文实例讲述了python实现简单的TCP代理服务器的方法,分享给大家供大家参考。 具体实现代码如下: # -*- coding: utf-8 -*- ''' filename:rtcp.py @desc: 利用python的socket端口转发,用于远程维护 如果连接不到...

    futures-async-stream:Rust和期货板条箱的异步流

    这是的#[async] for Futures 0.3循环的重新实现,并且是的实验性实现, 。 #![feature(proc_macro_hygiene, stmt_expr_attributes)] use futures :: stream :: Stream; use futures_async_stream :: for_await; ...

    Stream-2-Stream

    Stream-2-Stream是一个开源项目,其主要目的是实现流与流之间的通信,即Stream-to-Stream通信。这个项目的核心在于创建一个高效、可靠且灵活的框架,使得不同数据流能够实时、双向地交换信息。从提供的文件列表来看...

    Stream Deck SDK:记忆游戏 示例插件_C++_代码_下载

    本示例插件——MemoryGame,是利用Stream Deck SDK创建的一个全屏记忆游戏,展示了如何利用C++进行编程并实现跨平台功能。以下是对这个项目的详细解析: **C++编程** C++是一种广泛使用的面向对象的编程语言,以其...

Global site tag (gtag.js) - Google Analytics