...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

stdex.bind (2) - dmd 2.015

さっきのサンプルコードが全部通るようになったよ!
だけどstd.bindよりめちゃくちゃ短くなったと思って調べてみたら本来のstd.bindがやけに高機能だった件について.
次は型の調整に挑戦してみるよー.
あ,stdex.bindのソースは最後に張り付けときますね!
(追記:重複コードがたくさんあるけどtemplate/mixinではうまくいかない><;)

import std.stdio;
import stdex.bind;

T[] removeIf(alias cond, T)(T[] arr)
{
    T[] ret;
    foreach (v; arr)
        if (!cond(v)) ret ~= v;
    return ret;
}

void main()
{
    int[] arr = [1,2,3,4,5,6,7,8,9];
    auto less = (int i, int j){return i<j;};
    auto lessThan6 = bindAlias!(less)(_0, 6);
    auto greaterThan5 = bindAlias!(less)(5, _0);
    writefln(removeIf!(lessThan6)(arr));
    writefln(removeIf!(greaterThan5)(arr));
    
    static int sub(int a, int b){return a-b;};
    auto a = bindAlias!(sub)(_0, _1);
    auto b = bindAlias!(sub)(_1, _0);
    auto c = bindAlias!(sub)(_0, 5);
    auto d = bindAlias!(sub)(10, _0);
    auto e = bindAlias!(sub)(10, 5);
    auto f = bindAlias!(sub)(_0, _0);
    writefln(a(10,5)); // 5
    writefln(b(10,5)); // -5
    writefln(c(10)); // 5
    writefln(d(5)); // 5
    writefln(e()); // 5
    writefln(f(10)); // 0
    
    //////////////////////////////////////////////
    // 現在の制限
    //
    
    /+ // 型の調整はできない
    auto z = (double a, int b){return a/b;};
    writefln(bindAlias!(z)(_0,_0)(3)); // 0
    +/
    
    /+ // out/ref引数は扱えない
    auto f = (ref int a){a = 1;};
    int x = 2;
    f(x);
    writefln(x); // 2
    x = 3;
    auto fx = bindAlias!(f)(&x);
    fx();
    writefln(x); // 3
    +/
    
    /+ // 関数合成はできない
    auto g = (int x, int y){return x*y;}; // g(x, y) = x * y
    auto h = (int x){return 2*x;};  // h(x) = 2 * x
    auto gh = bindAlias!(h)( bindAlias!(g)(_0, 3) );
        // h(g(x, 3)) = h(x * 3) == 2 * (x * 3)
    writefln(gh(5)); // 30
    +/
}
/+
[6 7 8 9]
[1 2 3 4 5]
5
-5
5
5
5
0
+/
module stdex.bind;

import std.traits;
import std.typetuple;

struct DynArg(int i){static invariant uint id = i;}
DynArg!(0) _0;
DynArg!(1) _1;
DynArg!(2) _2;
DynArg!(3) _3;
DynArg!(4) _4;
DynArg!(5) _5;
DynArg!(6) _6;
DynArg!(7) _7;
DynArg!(8) _8;
DynArg!(9) _9;

template NewParameterTuple(alias func, TL...)
{
    alias typeof(TL) TL_;
    // 引数リストの何番目にDynArgがあるか
    enum indexDyn0 = indexOf!(DynArg!(0),TL_);
    //pragma(msg, indexDyn0);
    enum indexDyn1 = indexOf!(DynArg!(1),TL_);
    //pragma(msg, indexDyn1);
    enum indexDyn2 = indexOf!(DynArg!(2),TL_);
    //pragma(msg, indexDyn2);
    enum indexDyn3 = indexOf!(DynArg!(3),TL_);
    enum indexDyn4 = indexOf!(DynArg!(4),TL_);
    enum indexDyn5 = indexOf!(DynArg!(5),TL_);
    enum indexDyn6 = indexOf!(DynArg!(6),TL_);
    enum indexDyn7 = indexOf!(DynArg!(7),TL_);
    enum indexDyn8 = indexOf!(DynArg!(8),TL_);
    enum indexDyn9 = indexOf!(DynArg!(9),TL_);
    
    // 生関数の引数
    alias ParameterTypeTuple!(func) Params;
    //pragma(msg, Params[indexDyn0]);
    //pragma(msg, Params[0]);
    //pragma(msg, Params[1]);
    
    // DynArgを使っている部分に対応するfuncの引数の型だけを得る
    static if (indexDyn0 == -1)
        alias void NewParams;
    else static if (indexDyn1 == -1)
        alias TypeTuple!(Params[indexDyn0]) NewParams;
    else static if (indexDyn2 == -1)
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1]) NewParams;
    else static if (indexDyn3 == -1)
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1], Params[indexDyn2]) NewParams;
    else static if (indexDyn4 == -1)
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1], Params[indexDyn2], Params[indexDyn3]) NewParams;
    else static if (indexDyn5 == -1)
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1], Params[indexDyn2], Params[indexDyn3], Params[indexDyn4]) NewParams;
    else static if (indexDyn6 == -1)
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1], Params[indexDyn2], Params[indexDyn3], Params[indexDyn4], Params[indexDyn5]) NewParams;
    else static if (indexDyn7 == -1)
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1], Params[indexDyn2], Params[indexDyn3], Params[indexDyn4], Params[indexDyn5], Params[indexDyn6]) NewParams;
    else static if (indexDyn8 == -1)
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1], Params[indexDyn2], Params[indexDyn3], Params[indexDyn4], Params[indexDyn5], Params[indexDyn6], Params[indexDyn7]) NewParams;
    else static if (indexDyn9 == -1)
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1], Params[indexDyn2], Params[indexDyn3], Params[indexDyn4], Params[indexDyn5], Params[indexDyn6], Params[indexDyn7], Params[indexDyn8]) NewParams;
    else
        alias TypeTuple!(Params[indexDyn0], Params[indexDyn1], Params[indexDyn2], Params[indexDyn3], Params[indexDyn4], Params[indexDyn5], Params[indexDyn6], Params[indexDyn7], Params[indexDyn8], Params[indexDyn9]) NewParams;
    //
    //pragma(msg, NewParams);
}

class BoundFunc(alias func, TL...)
{
    alias ReturnType!(func) RetValue;
    alias NewParameterTuple!(func, TL).NewParams NewParams;
    //pragma(msg,TL);
    //pragma(msg,RetValue);
    //pragma(msg,Params);

    TL args;
    this(TL args)
    {
        this.args = args;
    }
    
    alias ParameterTypeTuple!(func) Args;
    
    static if (is(NewParams == void))
    {
        RetValue opCall()
        {
            return func(args);
        }
    }
    else
    {
        RetValue opCall(NewParams params)
        {
            static if (0 < Args.length)
            {
                static if (IsDynArg!(typeof(args[0])))
                    enum Arg0 = params[typeof(args[0]).id];
                else
                    enum Arg0 = args[0];
                //pragma(msg,typeof(args)[0].id);
                //pragma(msg,NewParameterTuple!(func, TL).indexDyn0);
            }
            static if (1 < Args.length)
            {
                static if (IsDynArg!(typeof(args[1])))
                    enum Arg1 = params[typeof(args[1]).id];
                else
                    enum Arg1 = args[1];
                //pragma(msg,typeof(args)[0].id);
                //pragma(msg,typeof(args)[1].id);
                //pragma(msg,Arg0);
                //pragma(msg,Arg1);
                //pragma(msg,NewParameterTuple!(func, TL).indexDyn1);
            }
            static if (2 < Args.length)
            {
                static if (IsDynArg!(typeof(args[2])))
                    enum Arg2 = params[typeof(args[2]).id];
                else
                    enum Arg2 = args[2];
            }
            static if (3 < Args.length)
            {
                static if (IsDynArg!(typeof(args[3])))
                    enum Arg3 = params[typeof(args[3]).id];
                else
                    enum Arg3 = args[3];
            }
            static if (4 < Args.length)
            {
                static if (IsDynArg!(typeof(args[4])))
                    enum Arg4 = params[typeof(args[4]).id];
                else
                    enum Arg4 = args[4];
            }
            static if (5 < Args.length)
            {
                static if (IsDynArg!(typeof(args[5])))
                    enum Arg5 = params[typeof(args[5]).id];
                else
                    enum Arg5 = args[5];
            }
            static if (6 < Args.length)
            {
                static if (IsDynArg!(typeof(args[6])))
                    enum Arg6 = params[typeof(args[6]).id];
                else
                    enum Arg6 = args[6];
            }
            static if (7 < Args.length)
            {
                static if (IsDynArg!(typeof(args[7])))
                    enum Arg7 = params[typeof(args[7]).id];
                else
                    enum Arg7 = args[7];
            }
            static if (8 < Args.length)
            {
                static if (IsDynArg!(typeof(args[8])))
                    enum Arg8 = params[typeof(args[8]).id];
                else
                    enum Arg8 = args[8];
            }
            static if (9 < Args.length)
            {
                static if (IsDynArg!(typeof(args[9])))
                    enum Arg9 = params[typeof(args[9]).id];
                else
                    enum Arg9 = args[9];
            }
            
            static if (Args.length == 1)
                return func(Arg0);
            else static if (Args.length == 2)
                return func(Arg0, Arg1);
            else static if (Args.length == 3)
                return func(Arg0, Arg1, Arg2, Arg3);
            else static if (Args.length == 4)
                return func(Arg0, Arg1, Arg2, Arg3, Arg4);
            else static if (Args.length == 5)
                return func(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5);
            else static if (Args.length == 6)
                return func(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6);
            else static if (Args.length == 7)
                return func(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7);
            else static if (Args.length == 8)
                return func(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8);
            else static if (Args.length == 9)
                return func(Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7, Arg8, Arg9);
            else
                static assert(false);// 生関数の引数が多すぎます
        }
    }
}

template IsDynArg(T)
{
    static if (is(T == DynArg!(0)) || is(T == DynArg!(1)) || is(T == DynArg!(2)) ||
        is(T == DynArg!(3)) || is(T == DynArg!(4)) || is(T == DynArg!(5)) ||
        is(T == DynArg!(6)) || is(T == DynArg!(7)) || is(T == DynArg!(8)) || is(T == DynArg!(9)))
    {
        enum IsDynArg = true;
    }
    else
    {
        enum IsDynArg = false;
    }
}

template bindAlias(alias func)
{
    BoundFunc!(func, TL) bindAlias(TL...)(TL args)
    {
        return new BoundFunc!(func, TL)(args);
    }
}