Immutable な片方向リスト

書いてみました。GCC 4.5.2 以降 (-std=c++0x) 専用です。
https://gist.github.com/891955
標準の sequence container に似せてありますが、次の点で大きく異なります。

  • 要素は変更できない
  • insert, erase, clear などの操作を行うと、リスト自身は変更されずに操作後のリストが返される

例を示します。(oven::identities はリストの中身を表示するために使用しています)

#include <iostream>
#include <iterator>
#include <pstade/oven/identities.hpp>
#include <pstade/oven/io.hpp>
#include "./slist.hpp"

namespace oven = pstade::oven;

int main()
{
    vitro::slist<int> const ns{0, 2, 3};

    std::cout << (ns | oven::identities) << '\n';

    std::cout <<
        (ns.insert(std::next(ns.begin()), 1) | oven::identities) << '\n';

    std::cout << (ns | oven::identities) << '\n';
}

実行結果

{0,2,3}
{0,1,2,3}
{0,2,3}

リストが変更されていないことがわかると思います。
リストを操作する、すなわち新しいリストを生成するとき、古い版と新しい版とで構造はできる限り共有されます。したがって、リストの先頭への操作は非常に安価に行えます。
この性質を利用して、関数型言語のような処理を書くことができます。以下は filter を実装する例です。

#include <iostream>
#include <boost/lambda/lambda.hpp>
#include <pstade/oven/identities.hpp>
#include <pstade/oven/io.hpp>
#include "./slist.hpp"

using namespace boost::lambda;
namespace oven = pstade::oven;

template <class Predicate, class T, class Allocator>
vitro::slist<T, Allocator>
filter(Predicate p, vitro::slist<T, Allocator> const &xs)
{
    return
    xs.empty() ?
        xs :
        !p(xs.front()) ?
            filter(p, xs.pop_front()) :
            filter(p, xs.pop_front()).push_front(xs.front());
}

int main()
{
    vitro::slist<int> const xs{0, 1, 2, 3, 4, 5, 6, 7};

    std::cout << (filter((_1 % 2) == 0, xs) | oven::identities);

    // {0,2,4,6}
}

私自身は使い方がよくわかっていませんが、こういうコンテナがあると面白いと思います。