squirrel_code @ ウィキ

続 無限リスト

最終更新:

squirrel_code

- view
管理者のみ編集可

続 無限リスト

last update: 2012/12/08 (Sat)

前回,C++0x を用いて基本的な無限リストを作成した.とはいえ,目につく問題も残っていた.例えばリストを扱うために常に shared_ptr を経由しなければならなかったり,リストの 2 番目を参照するために ls->cdr()->car といった不自然なメソッド呼び出しをする必要があったりした.これを改善しよう.

まず,常に shared_ptr となるのを防ぐには,cons セル全体をラッパクラスで覆えばよい.例えば以下のようになる.
template<typename T>
class list
{
    struct cell
    {
        T _car;
        std::function<std::shared_ptr<cell>()> _cdr;

        cell(T const & car, std::function<std::shared_ptr<cell>()> const & cdr)
            : _car(car), _cdr(cdr)
        {}
    };

    std::shared_ptr<cell> _cell;
};
std::list と名前が衝突する? そんなことは using namespace std する奴が悪いのだ(おい)それよりも問題は,この list をどうやって構築するかである.前回は cell 構造体そのものを lambda 式を用いて直接構築した.しかし今回は cell 構造体は private なので外部からは隠蔽されている.なので nat_from で「std::shared_ptr<cell> を返す lambda 式」を直接生成できない.

そこで,ユーザ側では 「list を返す lambda 式」を定義してもらって,それを「std::shared_ptr<cell> を返す lambda 式」に変換してやる必要がある.この変換をコンストラクタとして実装してやればよい.

ついでに,C++0x らしく cell 構造体はコピー・代入禁止とし,さらに car と cdr へのアクセサを実装しておく.
template <typename T>
class list
{
    struct cell
    {
        T car;
        std::function<std::shared_ptr<cell>()> cdr;

        cell(T const & car, std::function<std::shared_ptr<cell>()> const & cdr)
            : car(car), cdr(cdr)
        {
        }

        cell(cell const &) = delete;

        cell
        operator = (cell const &) = delete;
    };

    std::shared_ptr<cell> _cell;

    public:
    list()
    {
    }

    private:
    list(std::shared_ptr<cell> const & cell)
       : _cell(cell)
    {
    }

    public:
    list(T const & car, std::function<list<T>()> const & cdr)
        : _cell(new cell(car, [cdr]() { return cdr()._cell; }))
    {
    }

    T
    car() const { return _cell->car; }

    list
    cdr() const { return list(_cell->cdr()); }
};
これを用いて nat_from を実装すると
list<int>
nat_from(int k)
{
    return list<int>(k, [k]() { return nat_from(k + 1); });
}
おおー,ずっと自然になった.

有限リスト

これで自由自在に無限リストを使えるようになったであろうか.いや,まだ気が早い.例えば nat_from(1) で 1 から始まる増加列を生成できたとして,これの先頭に 0 を加えたい場合どうすればよいだろう.すでにある list を返す promise を改めて生成し,list 構築なんてめんどくさい.というか,有限だろうが無限だろうが,生成する方法は異なっても使うときは同じように使いたいのだ.これを実現しよう.

まず,有限リスト用の cons セルを定義する.これを無限リスト用の cons セルと混ぜて使いたいから,同じ基底クラスを持つ派生クラスとして定義すればよいだろう.ここで car 部は共通だから基底クラスが持てばよい.cdr 部は有限リストなら shared_ptr<cell> を,無限リストなら function<shared_ptr<cell>()> を保持し,アクセスされたら shared_ptr<cell> を返すようなアクセサを用意する.

template <typename T>
class list
{
    class cell_base
    {
        T _car;

        public:
        cell_base(T const & car)
            : _car(car)
        {
        }

        cell_base(cell_base const &) = delete;

        virtual ~cell_base()
        {
        }

        cell_base
        operator = (cell_base const &) = delete;

        T const &
        car() const { return _car; }

        virtual std::shared_ptr<cell_base>
        cdr() const = 0;
    };

    struct cell_impl_eager
        : public cell_base
    {
        std::shared_ptr<cell_base> _cdr;

        cell_impl_eager(T const & car, std::shared_ptr<cell_base> const & cdr)
            : cell_base(car), _cdr(cdr)
        {
        }

        std::shared_ptr<cell_base>
        cdr() const { return _cdr; }
    };

    struct cell_impl_delayed
        : public cell_base
    {
        std::function<std::shared_ptr<cell_base>()> _cdr;

        cell_impl_delayed(T const & car, std::function<std::shared_ptr<cell_base>()> const & cdr)
            : cell_base(car), _cdr(cdr)
        {
        }

        std::shared_ptr<cell_base>
        cdr() const { return _cdr(); }
    };
    
    std::shared_ptr<cell_base> _cell;

    public:
    list()
    {
    }

    private:
    list(std::shared_ptr<cell_base> const & cell)
        : _cell(cell)
    {
    }

    public:
    list(T const & car, list<T> const & cdr)
        : _cell(new cell_impl_eager(car, cdr._cell))
    {
    }

    list(T const & car, std::function<list<T>()> const & cdr)
        : _cell(new cell_impl_delayed(car, [cdr]() { return cdr()._cell; }))
    {
    }

    T const &
    car() const { return _cell->car(); }

    list
    cdr() const { return list(_cell->cdr()); }
};
試してみる.
list<int> ls = nat_from(1);
ls = list<int>(0, ls);
std::cout << ls.car() << std::endl; // 0
std::cout << ls.cdr().car() << std::endl; // 1
std::cout << ls.cdr().cdr().car() << std::endl; // 2
できちまったぜ.

&trackback()

参考



コメント

name
comment


関連ページ



関連ブログ

#blogsearch
記事メニュー
目安箱バナー