C++ 非多线程安全实现Stream Part 1

来源:互联网 发布:卡拉宝 知乎 编辑:程序博客网 时间:2024/06/08 04:32

Stream (lazy sequences)是一种非常强大的数据结构,他只在我们使用一个元素的时候才会真正进行计算。这个性质就允许实现无限长的序列!比如整数的Stream。

Stream这种数据结构在函数式语言中很常见,比如scala中可以这样实现一个无限的Fibonacci数列

val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 }
在c++语言中并没有这样的支持,这篇博客里我要介绍如何用实现Stream。

Suspension

首先需要实现一个有用的功能: 一个可以暂停function执行的类Susp,Susp这个类可以保存一个function f, 在需要结果的时候再去进行实际的计算.

Susp.hpp

#ifndef SUSP_HPP#define SUSP_HPP#include <functional>template<typename T>class Susp {public:    explicit Susp(std::function<T()> f)        : _thunk(thunkForce), _f(f)    {}    T const& get() { return _thunk(this); }private:T const& getMemo(){return _memo;}T const& setMemo(){_memo = _f();_thunk = thunkGet;return _memo;}static T const& thunkForce(Susp * susp){return susp->setMemo();}static T const& thunkGet(Susp * susp){return susp->getMemo();}T const& (*_thunk)(Susp *);    std::function<T()> _f;    mutable T _memo;};#endif
构造函数需要一个返回类型为T无需参数的函数f为参数。Susp的成员包括_f,f的复制;_memo用来保存计算过的结果;thunk一个函数指针通过修改这个指针的值可以达到第一次调用get的时候进行计算,之后对get的调用仅仅读取缓存。

Stream

Stream可以是空容器,或者包含一个Cell。一个Cell,为了达到可以获得一个value和Stream去掉第一个元素之后的尾巴的目的,需要包含一个value和一个Stream。

因为在概念上Stream和Cell都是immutable的,所以一个Stream包含一个表示Cell的shared_ptr,是安全的。

stream.hpp

#ifndef STREAM_HPP#define STREAM_HPP#include "Susp.hpp"#include <memory>#include <functional>template<class T> class Stream;template<class T>class Cell{public:Cell();Cell(T v, Stream<T> const& tail);explicit Cell(T v);T val() const;Stream<T> pop_front() const;private:T _v;Stream<T> _tail;};template<typename T>class Stream{public:Stream(std::function<Cell<T>()> f): _lazyCell(std::make_shared<Susp<Cell<T> > >(Susp<Cell<T> >(f))) {}Stream() {}~Stream() {}Stream(Stream const & stm) = default;Stream(Stream && stm): _lazyCell(std::move(stm._lazyCell)){}Stream& operator=(Stream && stm){_lazyCell = std::move(stm._lazyCell);return *this;}T get() const{return _lazyCell->get().val();}Stream<T> pop_front() const{return _lazyCell->get().pop_front();}bool is_empty() const{return !_lazyCell;}private:std::shared_ptr<Susp<Cell<T> > > _lazyCell;};template<class T> Cell<T>::Cell() {}template<class T> Cell<T>::Cell(T v, Stream<T> const& tail): _v(v), _tail(tail) {}template<class T> Cell<T>::Cell(T v) : _v(v) {}template<class T> T Cell<T>::val() const{return _v;}template<class T> Stream<T> Cell<T>::pop_front() const{return _tail;}#endif

一个Stream包含一个可以获得Cell的Susp,在第一次调用get或者pop_front的时候,才真正计算Cell。

看一个简单的例子Application.cpp

#include "stream.hpp"#include <iostream>Stream<int> ints(int n, int m){if (n > m){return Stream<int>();}return Stream<int>([n, m]() {return Cell<int>(n, ints(n+1, m));});}int main(int argc, char ** argv){Stream<int> stm = ints(1,10);while (!stm.is_empty()) {int a = stm.get();stm = stm.pop_front();std::cout << a << std::endl;}return 0;}

在例子中,构建了一个从n开始到m的Stream。Stream的初始化函数返回n,和一个从n+1到m的Stream。

附上Makefile

CC=g++CFLAGS=-std=c++11 -cLDFLAGS=SOURCES=Application.cppOBJECTS=$(SOURCES:.cpp=.o)EXECUTABLE=streamall: $(SOURCES) $(EXECUTABLE)$(EXECUTABLE): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $@.cpp.o:$(CC) $(CFLAGS) $< -o $@.PHONY: cleanclean:rm -f *~ *.o stream

这篇博客是我读博客的学习记录详情请见 http://bartoszmilewski.com/2014/04/21/getting-lazy-with-c/

在part 2中,我会记录Stream的用法和性质。

0 0
原创粉丝点击