Wolfgang Thallergcc16 merge934c3885 months agoHistory
2226 lines · 60.8 KB · D
RawDownload
// Written in the D programming language. /**Functions that manipulate other functions. This module provides functions for compile time function composition. Thesefunctions are helpful when constructing predicates for the algorithms in$(MREF std, algorithm) or $(MREF std, range). $(SCRIPT inhibitQuickIndex = 1;)$(DIVC quickindex,$(BOOKTABLE ,$(TR $(TH Function Name) $(TH Description))    $(TR $(TD $(LREF adjoin))        $(TD Joins a couple of functions into one that executes the original        functions independently and returns a tuple with all the results.    ))    $(TR $(TD $(LREF bind))        $(TD Passes the fields of a struct as arguments to a function.    ))    $(TR $(TD $(LREF compose), $(LREF pipe))        $(TD Join a couple of functions into one that executes the original        functions one after the other, using one function's result for the next        function's argument.    ))    $(TR $(TD $(LREF ctEval))        $(TD Enforces the evaluation of an expression during compile-time.    ))    $(TR $(TD $(LREF curry))        $(TD Converts a multi-argument function into a series of single-argument        functions. `f(x, y) == curry(f)(x)(y)`    ))    $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo))        $(TD Ready-made predicate functions to compare two values.    ))    $(TR $(TD $(LREF memoize))        $(TD Creates a function that caches its result for fast re-evaluation.    ))    $(TR $(TD $(LREF not))        $(TD Creates a function that negates another.    ))    $(TR $(TD $(LREF partial))        $(TD Creates a function that binds the first argument of a given function        to a given value.    ))    $(TR $(TD $(LREF reverseArgs))        $(TD Predicate that reverses the order of its arguments.    ))    $(TR $(TD $(LREF toDelegate))        $(TD Converts a callable to a delegate.    ))    $(TR $(TD $(LREF unaryFun), $(LREF binaryFun))        $(TD Create a unary or binary function from a string. Most often        used when defining algorithms on ranges.    )))) Copyright: Copyright Andrei Alexandrescu 2008 - 2009.License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).Authors:   $(HTTP erdani.org, Andrei Alexandrescu)Source:    $(PHOBOSSRC std/functional.d)*//*         Copyright Andrei Alexandrescu 2008 - 2009.Distributed under the Boost Software License, Version 1.0.   (See accompanying file LICENSE_1_0.txt or copy at         http://www.boost.org/LICENSE_1_0.txt)*/module std.functional; import std.meta : AliasSeq, Reverse;import std.traits : isCallable, Parameters;import std.conv : toCtString; import std.internal.attributes : betterC; public import core.lifetime : forward; private template needOpCallAlias(alias fun){    /* Determine whether or not unaryFun and binaryFun need to alias to fun or     * fun.opCall. Basically, fun is a function object if fun(...) compiles. We     * want is(unaryFun!fun) (resp., is(binaryFun!fun)) to be true if fun is     * any function object. There are 4 possible cases:     *     *  1) fun is the type of a function object with static opCall;     *  2) fun is an instance of a function object with static opCall;     *  3) fun is the type of a function object with non-static opCall;     *  4) fun is an instance of a function object with non-static opCall.     *     * In case (1), is(unaryFun!fun) should compile, but does not if unaryFun     * aliases itself to fun, because typeof(fun) is an error when fun itself     * is a type. So it must be aliased to fun.opCall instead. All other cases     * should be aliased to fun directly.     */    static if (is(typeof(fun.opCall) == function))    {        enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () {            return fun(Parameters!fun.init);        });    }    else        enum needOpCallAlias = false;} /**Transforms a `string` representing an expression into a unaryfunction. The `string` must either use symbol name `a` asthe parameter or provide the symbol via the `parmName` argument. Params:    fun = a `string` or a callable    parmName = the name of the parameter if `fun` is a string. Defaults    to `"a"`.Returns:    If `fun` is a `string`, a new single parameter function     If `fun` is not a `string`, an alias to `fun`.*/template unaryFun(alias fun, string parmName = "a"){    static if (is(typeof(fun) : string))    {        static if (!fun._ctfeMatchUnary(parmName))        {            import std.algorithm, std.conv, std.exception, std.math, std.range, std.string;            import std.meta, std.traits, std.typecons;        }        auto unaryFun(ElementType)(auto ref ElementType __a)        {            mixin("alias " ~ parmName ~ " = __a ;");            return mixin(fun);        }    }    else static if (needOpCallAlias!fun)    {        // https://issues.dlang.org/show_bug.cgi?id=9906        alias unaryFun = fun.opCall;    }    else    {        alias unaryFun = fun;    }} ///@safe unittest{    // Strings are compiled into functions:    alias isEven = unaryFun!("(a & 1) == 0");    assert(isEven(2) && !isEven(1));} @safe unittest{    static int f1(int a) { return a + 1; }    static assert(is(typeof(unaryFun!(f1)(1)) == int));    assert(unaryFun!(f1)(41) == 42);    int f2(int a) { return a + 1; }    static assert(is(typeof(unaryFun!(f2)(1)) == int));    assert(unaryFun!(f2)(41) == 42);    assert(unaryFun!("a + 1")(41) == 42);    //assert(unaryFun!("return a + 1;")(41) == 42);     int num = 41;    assert(unaryFun!"a + 1"(num) == 42);     // https://issues.dlang.org/show_bug.cgi?id=9906    struct Seen    {        static bool opCall(int n) { return true; }    }    static assert(needOpCallAlias!Seen);    static assert(is(typeof(unaryFun!Seen(1))));    assert(unaryFun!Seen(1));     Seen s;    static assert(!needOpCallAlias!s);    static assert(is(typeof(unaryFun!s(1))));    assert(unaryFun!s(1));     struct FuncObj    {        bool opCall(int n) { return true; }    }    FuncObj fo;    static assert(!needOpCallAlias!fo);    static assert(is(typeof(unaryFun!fo)));    assert(unaryFun!fo(1));     // Function object with non-static opCall can only be called with an    // instance, not with merely the type.    static assert(!is(typeof(unaryFun!FuncObj)));} /**Transforms a `string` representing an expression into a binary function. The`string` must either use symbol names `a` and `b` as the parameters orprovide the symbols via the `parm1Name` and `parm2Name` arguments. Params:    fun = a `string` or a callable    parm1Name = the name of the first parameter if `fun` is a string.    Defaults to `"a"`.    parm2Name = the name of the second parameter if `fun` is a string.    Defaults to `"b"`.Returns:    If `fun` is not a string, `binaryFun` aliases itself away to    `fun`.*/template binaryFun(alias fun, string parm1Name = "a",        string parm2Name = "b"){    static if (is(typeof(fun) : string))    {        static if (!fun._ctfeMatchBinary(parm1Name, parm2Name))        {            import std.algorithm, std.conv, std.exception, std.math, std.range, std.string;            import std.meta, std.traits, std.typecons;        }        auto binaryFun(ElementType1, ElementType2)            (auto ref ElementType1 __a, auto ref ElementType2 __b)        {            mixin("alias "~parm1Name~" = __a ;");            mixin("alias "~parm2Name~" = __b ;");            return mixin(fun);        }    }    else static if (needOpCallAlias!fun)    {        // https://issues.dlang.org/show_bug.cgi?id=9906        alias binaryFun = fun.opCall;    }    else    {        alias binaryFun = fun;    }} ///@safe unittest{    alias less = binaryFun!("a < b");    assert(less(1, 2) && !less(2, 1));    alias greater = binaryFun!("a > b");    assert(!greater("1", "2") && greater("2", "1"));} @safe unittest{    static int f1(int a, string b) { return a + 1; }    static assert(is(typeof(binaryFun!(f1)(1, "2")) == int));    assert(binaryFun!(f1)(41, "a") == 42);    string f2(int a, string b) { return b ~ "2"; }    static assert(is(typeof(binaryFun!(f2)(1, "1")) == string));    assert(binaryFun!(f2)(1, "4") == "42");    assert(binaryFun!("a + b")(41, 1) == 42);    //@@BUG    //assert(binaryFun!("return a + b;")(41, 1) == 42);     // https://issues.dlang.org/show_bug.cgi?id=9906    struct Seen    {        static bool opCall(int x, int y) { return true; }    }    static assert(is(typeof(binaryFun!Seen)));    assert(binaryFun!Seen(1,1));     struct FuncObj    {        bool opCall(int x, int y) { return true; }    }    FuncObj fo;    static assert(!needOpCallAlias!fo);    static assert(is(typeof(binaryFun!fo)));    assert(unaryFun!fo(1,1));     // Function object with non-static opCall can only be called with an    // instance, not with merely the type.    static assert(!is(typeof(binaryFun!FuncObj)));} // skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'.private uint _ctfeSkipOp(ref string op){    if (!__ctfe) assert(false);    import std.ascii : isASCII, isAlphaNum;    immutable oldLength = op.length;    while (op.length)    {        immutable front = op[0];        if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.'))            op = op[1..$];        else            break;    }    return oldLength != op.length;} // skip all digitsprivate uint _ctfeSkipInteger(ref string op){    if (!__ctfe) assert(false);    import std.ascii : isDigit;    immutable oldLength = op.length;    while (op.length)    {        immutable front = op[0];        if (front.isDigit())            op = op[1..$];        else            break;    }    return oldLength != op.length;} // skip nameprivate uint _ctfeSkipName(ref string op, string name){    if (!__ctfe) assert(false);    if (op.length >= name.length && op[0 .. name.length] == name)    {        op = op[name.length..$];        return 1;    }    return 0;} // returns 1 if `fun` is trivial unary functionprivate uint _ctfeMatchUnary(string fun, string name){    if (!__ctfe) assert(false);    fun._ctfeSkipOp();    for (;;)    {        immutable h = fun._ctfeSkipName(name) + fun._ctfeSkipInteger();        if (h == 0)        {            fun._ctfeSkipOp();            break;        }        else if (h == 1)        {            if (!fun._ctfeSkipOp())                break;        }        else            return 0;    }    return fun.length == 0;} @safe unittest{    static assert(!_ctfeMatchUnary("sqrt(ё)", "ё"));    static assert(!_ctfeMatchUnary("ё.sqrt", "ё"));    static assert(!_ctfeMatchUnary(".ё+ё", "ё"));    static assert(!_ctfeMatchUnary("_ё+ё", "ё"));    static assert(!_ctfeMatchUnary("ёё", "ё"));    static assert(_ctfeMatchUnary("a+a", "a"));    static assert(_ctfeMatchUnary("a + 10", "a"));    static assert(_ctfeMatchUnary("4 == a", "a"));    static assert(_ctfeMatchUnary("2 == a", "a"));    static assert(_ctfeMatchUnary("1 != a", "a"));    static assert(_ctfeMatchUnary("a != 4", "a"));    static assert(_ctfeMatchUnary("a< 1", "a"));    static assert(_ctfeMatchUnary("434 < a", "a"));    static assert(_ctfeMatchUnary("132 > a", "a"));    static assert(_ctfeMatchUnary("123 >a", "a"));    static assert(_ctfeMatchUnary("a>82", "a"));    static assert(_ctfeMatchUnary("ё>82", "ё"));    static assert(_ctfeMatchUnary("ё[ё(ё)]", "ё"));    static assert(_ctfeMatchUnary("ё[21]", "ё"));} // returns 1 if `fun` is trivial binary functionprivate uint _ctfeMatchBinary(string fun, string name1, string name2){    if (!__ctfe) assert(false);    fun._ctfeSkipOp();    for (;;)    {        immutable h = fun._ctfeSkipName(name1) + fun._ctfeSkipName(name2) + fun._ctfeSkipInteger();        if (h == 0)        {            fun._ctfeSkipOp();            break;        }        else if (h == 1)        {            if (!fun._ctfeSkipOp())                break;        }        else            return 0;    }    return fun.length == 0;} @safe unittest{     static assert(!_ctfeMatchBinary("sqrt(ё)", "ё", "b"));    static assert(!_ctfeMatchBinary("ё.sqrt", "ё", "b"));    static assert(!_ctfeMatchBinary(".ё+ё", "ё", "b"));    static assert(!_ctfeMatchBinary("_ё+ё", "ё", "b"));    static assert(!_ctfeMatchBinary("ёё", "ё", "b"));    static assert(_ctfeMatchBinary("a+a", "a", "b"));    static assert(_ctfeMatchBinary("a + 10", "a", "b"));    static assert(_ctfeMatchBinary("4 == a", "a", "b"));    static assert(_ctfeMatchBinary("2 == a", "a", "b"));    static assert(_ctfeMatchBinary("1 != a", "a", "b"));    static assert(_ctfeMatchBinary("a != 4", "a", "b"));    static assert(_ctfeMatchBinary("a< 1", "a", "b"));    static assert(_ctfeMatchBinary("434 < a", "a", "b"));    static assert(_ctfeMatchBinary("132 > a", "a", "b"));    static assert(_ctfeMatchBinary("123 >a", "a", "b"));    static assert(_ctfeMatchBinary("a>82", "a", "b"));    static assert(_ctfeMatchBinary("ё>82", "ё", "q"));    static assert(_ctfeMatchBinary("ё[ё(10)]", "ё", "q"));    static assert(_ctfeMatchBinary("ё[21]", "ё", "q"));     static assert(!_ctfeMatchBinary("sqrt(ё)+b", "b", "ё"));    static assert(!_ctfeMatchBinary("ё.sqrt-b", "b", "ё"));    static assert(!_ctfeMatchBinary(".ё+b", "b", "ё"));    static assert(!_ctfeMatchBinary("_b+ё", "b", "ё"));    static assert(!_ctfeMatchBinary("ba", "b", "a"));    static assert(_ctfeMatchBinary("a+b", "b", "a"));    static assert(_ctfeMatchBinary("a + b", "b", "a"));    static assert(_ctfeMatchBinary("b == a", "b", "a"));    static assert(_ctfeMatchBinary("b == a", "b", "a"));    static assert(_ctfeMatchBinary("b != a", "b", "a"));    static assert(_ctfeMatchBinary("a != b", "b", "a"));    static assert(_ctfeMatchBinary("a< b", "b", "a"));    static assert(_ctfeMatchBinary("b < a", "b", "a"));    static assert(_ctfeMatchBinary("b > a", "b", "a"));    static assert(_ctfeMatchBinary("b >a", "b", "a"));    static assert(_ctfeMatchBinary("a>b", "b", "a"));    static assert(_ctfeMatchBinary("ё>b", "b", "ё"));    static assert(_ctfeMatchBinary("b[ё(-1)]", "b", "ё"));    static assert(_ctfeMatchBinary("ё[-21]", "b", "ё"));} //undocumentedtemplate safeOp(string S)if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!="){    import std.traits : isIntegral;    private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure    if (isIntegral!ElementType1 && isIntegral!ElementType2)    {        import std.traits : CommonType;        alias T = CommonType!(ElementType1, ElementType2);        return mixin("cast(T)a "~S~" cast(T) b");    }     bool safeOp(T0, T1)(auto ref T0 a, auto ref T1 b)    {        import std.traits : mostNegative;        static if (isIntegral!T0 && isIntegral!T1 &&                   (mostNegative!T0 < 0) != (mostNegative!T1 < 0))        {            static if (S == "<=" || S == "<")            {                static if (mostNegative!T0 < 0)                    immutable result = a < 0 || unsafeOp(a, b);                else                    immutable result = b >= 0 && unsafeOp(a, b);            }            else            {                static if (mostNegative!T0 < 0)                    immutable result = a >= 0 && unsafeOp(a, b);                else                    immutable result = b < 0 || unsafeOp(a, b);            }        }        else        {            static assert(is(typeof(mixin("a "~S~" b"))),                "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ ".");             immutable result = mixin("a "~S~" b");        }        return result;    }} @safe unittest //check user defined types{    import std.algorithm.comparison : equal;    struct Foo    {        int a;        auto opEquals(Foo foo)        {            return a == foo.a;        }    }

Lines 1–500 of 2226.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t