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

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 -c
LDFLAGS=
SOURCES=Application.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=stream

all: $(SOURCES) $(EXECUTABLE)
	
$(EXECUTABLE): $(OBJECTS) 
	$(CC) $(LDFLAGS) $(OBJECTS) -o $@

.cpp.o:
	$(CC) $(CFLAGS) $< -o $@

.PHONY: clean
clean:
	rm -f *~ *.o stream

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值