Project Euler 1(2)

Pythonでisum,ifilter,irangeを自作して、

def isum(a):
    s = 0
    for e in a:
        s += e
    return s

def ifilter(f, a):
    for e in a:
        if f(e):
            yield e

def irange(begin, end):
    n = begin
    while n < end:
        yield n
        n += 1

print isum(ifilter(lambda n: n % 3 == 0 or n % 5 == 0, irange(1, 1000)))

このようにC++でも書きたい。

ジェネレータは関数オブジェクトで実現する。まずは、range。

template<typename T = int>
class range {
    T   current;
    T   end;
    T   delta;
    
public:
    range(T b, T e, T d = 1) : current(b), end(e), delta(d) { }
    range(T e) : end(e) {
        current = 0;
        delta = d;
    }
    range() {
        current = end = delta = 0;
    }
    T next() {
        if(current < end) {
            T   v = current;
            current += 1;
            return v;
        }
        else {
            throw(1);
        }
    }
};

Pythonと同様に、next()で値を出す。最後まで来たら、これもPython同様例外を投げる。Python同様intだけでいいとも思ったが、一応テンプレートを使っておく。

次にsum。

template<typename T>
auto sum(T& o) -> decltype(o.next()) {
    auto    s = 0;
    try {
        while(true) {
            s += o.next();
        }
    }
    catch(int e) {
        return s;
    }
}

返り値の型を指定は、上のようにdecltypeを使うと回避できるらしい。
filterはrangeと同様に作って、結局このようになった。

#include <iostream>

using namespace std;

const int   N = 1000;

// sumとrangeは省略

template<typename T, typename U, typename V>
class filter {
    T   pred;
    U   obj;
    
public:
    filter(T p, U o) : pred(p), obj(o) { }
    
    V next() {
        decltype(obj.next())    v;
        do {
            v = obj.next();
        } while(!pred(v));
        return v;
    }
};

int main() {
    struct f {
        bool operator ()(int n) const {
            return n % 3 == 0 || n % 5 == 0;
        }
    };
    
    cout << sum(filter<struct f,range<>,int>(
                        f(), range<>(1, N))) << endl;
}

うーむ。