-
Notifications
You must be signed in to change notification settings - Fork 3
Function Utility
- 関数の合成
- 関数のカリー化
- 関数に引数を渡す演算子と補助クラス群
の3つが含まれるライブラリ. 個別にインクルードすることもできるが, cranberries/func_util.hppをインクルードすればすべて含まれる.
cranberries/func_util/composition.hpp
- composited<F,G>
宣言:
template < class F, class G >
class composited;コンストラクタ:
constexpr composited(const F& f, const G& g) noexcept
constexpr composited(F&& f, const G& g) noexcept
constexpr composited(const F& f, G&& g) noexcept
constexpr composited(F&& f, G&& g) noexcept
constexpr composited() noexcept
メンバ関数:
decltype(auto) operator()(Args...)
Function Call.
合成関数に引数を渡して評価した結果を返す.
( F(G(Args...))を実行する ).
template < class... Args >
constexpr decltype(auto) operator()(Args&&... args) &
template < class... Args >
constexpr decltype(auto) operator()(Args&&... args) const &
template < class... Args >
constexpr decltype(auto) operator()(Args&&... args) &&
template < class... Args >
constexpr decltype(auto) operator()(Args&&... args) const &&composition(関数1, 関数2, 関数3, ... , 関数N)
宣言:
template < class F, class G >
constexpr
composited<std::decay_t<F>, std::decay_t<G>>
composition(F&& f, G&& g) noexcept;
template < class F, class G, class... Rest >
constexpr
auto
composition(F&& f, G&& g, Rest&&... rest) noexcept;作用:
関数1(関数2(関数3(...)))のような合成関数をつくる.
例:
int f(int a) { return a + 1; }
int g(int a) { return a * a; }
auto h = cranberries::composition(g, f);
h(2); // g(f(2)) と同じcranberries/func_util/curry.hpp
- curried<Fn>
定義:
template<typename Fn>
class curriedコンストラクタ:
constexpr curried(const Fn& fn) noexcept;
constexpr curried(Fn&& fn) noexcept;
constexpr curried() noexcept;メンバ関数:
decltype(auto) operator()(Args...)
カリー化された関数に引数を部分適用をした関数を返す.
引数なしで呼ばれたときに関数を評価する.
template<typename ...Args>
constexpr decltype(auto) operator()(Args&& ...args) & noexcept;
template<typename ...Args>
constexpr decltype(auto) operator()(Args&& ...args) && noexcept;
template<typename ...Args>
constexpr decltype(auto) operator()(Args&& ...args) const& noexcept;
template<typename ...Args>
constexpr decltype(auto) operator()(Args&& ...args) const&& noexcept;- curried_<N, Fn>
定義:
template<size_t N, typename Fn>
class curried_コンストラクタ:
constexpr curried(const Fn& fn) noexcept;
constexpr curried(Fn&& fn) noexcept;
constexpr curried() noexcept;メンバ関数:
decltype(auto) operator()(Args...)
カリー化された関数に引数を部分適用をした関数を返す.
N個目の部分適用されたときに関数を評価する.
template<typename ...Args>
constexpr decltype(auto) operator()(Args&& ...args) & noexcept;
template<typename ...Args>
constexpr decltype(auto) operator()(Args&& ...args) && noexcept;
template<typename ...Args>
constexpr decltype(auto) operator()(Args&& ...args) const& noexcept;
template<typename ...Args>
constexpr decltype(auto) operator()(Args&& ...args) const&& noexcept;curry(関数)
宣言:
template < class F >
inline constexpr
curried<std::decay_t<F>>
curry(F&& f) noexcept;
作用:
カリー化された関数を得る.
クラスcurriedは遅延評価戦略なので, 引数を適用したあとに引数なしでoperator()を呼ぶことで評価される.
例:
struct Sum
{
template < class Head, class... Tail >
constexpr auto operator()(Head&& head, Tail&&... tail) const{
auto result = head;
(void)std::initializer_list<int>{ (void( result += tail),0)... };
return result;
}
};
constexpr auto sum = Sum{};
// 可変長引数関数のカリー化
constexpr auto curried_sum = cranberries::curry(sum);
// 最後に引数なしでoperator()を呼ばないと評価されない
static_assert(curried_sum(1)(2)(3)(4)(5)() == 15, "");
// 一度に複数の引数を渡せるようにもした(カリー化とはいったい...)
static_assert(curried_sum(1, 2, 3)(4)(5)() == 15, "");curry_<N>( 関数 )
作用:
渡せる引数の上限が決まっているカリー化された関数を得る.
N個の引数を渡すと評価される.
例:
struct Sum
{
template < class Head, class... Tail >
constexpr auto operator()(Head&& head, Tail&&... tail) const{
auto result = head;
(void)std::initializer_list<int>{ (void( result += tail),0)... };
return result;
}
};
constexpr int test(int,int,int) { return 5; }
// 引数を固定したカリー化, N個目の引数が適用されると自動で戻り値が返ってくる
constexpr int value = cranberries::curry_<4>(sum)(1)(2)(3)(4);
static_assert(value==10, "");
// 関数ポインタから自動で引数の数を推論できる
static_assert(cranberries::curry_(test)(0)(0)(0)==5, "");関数に引数を(いろいろな方法で)適用するための演算子オーバーロードと補助クラス群が含まれる.
これらは cranberries::func_util 名前空間に存在する.
このライブラリは黒魔術と言ってよい実装になっている + MSVCとそれ以外で宣言が異なるので宣言・定義など省略する.
All Apply 演算子 <<=
decltype(auto) operator <<= ( F, Tuple )
decltype(auto) operator <<= ( F, A )
作用:
f <<= tuple
としたとき, tupleの要素をすべて f 渡して f を呼び出す.
C++17(Cranberries.Utilityにもあるけど)のapply(f, tuple)と同様の作用を持つ.
単一要素の場合, tupleでなくても良い.
f <<= 2
のようにできる.
Each Apply 演算子 |=
decltype(auto) operator |= ( F, Tuple )
作用:
f |= tuple
としたとき, tupleの要素を一つづつ f に適用して実行する.
戻り値 f のすべての呼び出し結果が void でないなら tuple で返る.
一つでも void を返す f のオーバーロードがあれば 戻り値は void になる.
operator |= に渡すことで様々な条件で関数に引数を適用できるようになる.
共通してstatic関数bindがあり, これを呼び出して値をbindするとoperator |= に渡すことができるプロキシクラスが生成される.
chunk<N>
f |= chunk<N>::bind(args...)
とすると args... をN個ずつ f に渡すことができる.
f は sizeof...(args)/N 回呼ばれ, 末尾のあまりは読み捨てられる.
f , args... が共にコンパイル時定数のとき, コンパイル時計算可能.
例:
int main() {
auto println = [](auto&& head, auto&&... tail) {
std::cout << head;
(void)cranberries::Swallows{ (void(std::cout << " " << tail),0)... };
std::cout << std::endl;
};
auto add = [](auto _1, auto_2) { return _1 + _2; }
using namespace cranberries::func_util;
println <<= add |= chunk<2>::bind(1, 2, 3, 4, 5, 6, 7); // 3 7 11
/* [ Note:
add |= chunk<2>::apply(1, 2, 3, 4, 5, 6, 7)
returns tuple like bellow
[add(1,2), add(3,4), add(5,6)] => [3, 7, 11]
and 7 is discarded.
- end note]*/
}adjacent<N>
f |= adjacent<N>::bind(args...)
とすると args... を隣接N個ずつ f に渡すことができる.
f は sizeof...(args)-N+1 回呼ばれる.
Nより sizeof...(args) が少ないbindの呼び出しはstatic_assertになる.
f , args... が共にコンパイル時定数のとき, コンパイル時計算可能.
例:
int main() {
auto println = [](auto&& head, auto&&... tail) {
std::cout << head;
(void)cranberries::Swallows{ (void(std::cout << " " << tail),0)... };
std::cout << std::endl;
};
auto add = [](auto _1, auto_2) { return _1 + _2; }
using namespace cranberries::func_util;
println <<= add |= adjacent<2>::bind(1, 2, 3, 4, 5); // 3 5 7 9
/* [ Note:
add |= chunk<2>::apply(1, 2, 3, 4, 5)
returns tuple like bellow
[add(1,2), add(2,3), add(3,4), add(4,5)] => [3, 5, 7, 9]
- end note]*/
}permutation<R>
f |= permutaion<R>::bind(args...)
とすると args... からR個選ぶ順列をすべて f に適用してくれる.
N = sizeof...(args) としたとき,
f は N! / (N-R)! 回呼ばれる.
Rはフォルト(permutaion<>)でNになるようになっている.
Rより sizeof...(args) が少ないbindの呼び出しはstatic_assertになる.
args... がすべて同じ型でない場合もstatic_assertになる.
args... は比較可能でなければならず, ソート済みでなければならない.
イテレータのスワップを行うためコンパイル時計算不可能.
例:
int main() {
auto println = [](auto&& head, auto&&... tail) {
std::cout << head;
(void)cranberries::Swallows{ (void(std::cout << " " << tail),0)... };
std::cout << std::endl;
};
println |= permutation<2>::bind(1, 2, 3);
// 1 2
// 1 3
// 2 1
// 2 3
// 3 1
// 3 2
}combination<R>
f |= combination<R>::bind(args...)
とすると args... からR個選ぶ組み合わせをすべて f に適用してくれる.
N = sizeof...(args) としたとき,
f は N! / (R!(N-R)!) 回呼ばれる.
Rはデフォルト(permutaion<>)でNになるようになっている.
Rより sizeof...(args) が少ないbindの呼び出しはstatic_assertになる.
args... がすべて同じ型でない場合もstatic_assertになる.
args... は比較可能でなければならず, ソート済みでなければならない.
イテレータのスワップを行うためコンパイル時計算不可能.
例:
int main() {
auto println = [](auto&& head, auto&&... tail) {
std::cout << head;
(void)cranberries::Swallows{ (void(std::cout << " " << tail),0)... };
std::cout << std::endl;
};
println |= combination<2>::bind(1, 2, 3);
// 1 2
// 1 3
// 2 3
}chunk<N, randomized<Times>>
f |= chunk<N, randomized<Times>>::bind(args...)
とすると args... からN個をランダムに選んでTimes回 f に渡すことができる.
f は Times 回呼ばれる.
内部でstd::mt19937が状態を持つので, コンパイル時計算不可能.
例:
int main() {
auto println = [](auto&& head, auto&&... tail) {
std::cout << head;
(void)cranberries::Swallows{ (void(std::cout << " " << tail),0)... };
std::cout << std::endl;
};
auto add = [](auto _1, auto _2) { return _1 + _2; }
using namespace cranberries::func_util;
// (一例)
// 5 7 4 8 7 6 3 10 8 6
println <<= add |= chunk<2, randomized<10>>::bind(1, 2, 3, 4, 5, 6, 7);
}