unfold(2)

前回のunfoldは無限列しか出しません。それをtakewhile止めています。しかし、例えば整数を10進数に分解することを考えます。10で割った余りを出していくと、

shared_ptr<cIterable<int>> digits(int n) {
    auto    f = [] (int n) { return pair<int,int>(n % 10, n / 10); };
    return unfold(f, n);
}

int main() {
    cout << digits(123) << endl;
}

3 2 1 0 0 0 …と延々出力されてしまい、止める方法がありません。
HaskellではMaybeモナドを使うのですが、

import Data.List
digits = unfoldr (\n -> if n == 0 then Nothing else Just (mod n 10, div n 10))
main = print (digits 123)

ここでは停止条件を別の引数で与えることにしましょう。状態sの関数を与えて、trueだったらその手前で終わりということにします。その引数を与えなければ止まらないとします。

template<typename T, typename U, typename V, typename W>
class cUnfold : public cIterable<T> {
    T   val;
    U   stat;
    V   func;
    W   pred;
    
public:
    cUnfold(V f, U s, W p) : stat(s), func(f), pred(p) { }
    
    bool exists_next() {
        bool    b = !pred(stat);
        if(b) {
            pair<T,U>   p = func(stat);
            val = p.first;
            stat = p.second;
        }
        return b;
    }
    T value() const { return val; }
};

template<typename U, typename V, typename W>
auto unfold(V f, U s, W p) -> shared_ptr<cIterable<decltype(f(s).first)>> {
    typedef decltype(f(s).first)    T;
    return shared_ptr<cIterable<T>>(new cUnfold<T,U,V,W>(f, s, p));
}

template<typename U, typename V>
auto unfold(V f, U s) -> shared_ptr<cIterable<decltype(f(s).first)>> {
    typedef decltype(f(s).first)    T;
    auto    pred = [] (U s) { return false; };
    typedef decltype(pred)  W;
    return shared_ptr<cIterable<T>>(new cUnfold<T,U,V,W>(f, s, pred));
}

これで、3 2 1と出力されるようになります。

shared_ptr<cIterable<int>> digits(int n) {
    auto    f = [] (int n) { return pair<int,int>(n % 10, n / 10); };
    auto    pred = [] (int s) { return s == 0; };
    return unfold(f, n, pred);
}

int main() {
    cout << digits(123) << endl;
}