| // Written in the D programming language. | |
| /** | |
| Functions that manipulate other functions. | |
| This module provides functions for compile time function composition. These | |
| functions 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 unary | |
| function. The `string` must either use symbol name `a` as | |
| the 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 or | |
| provide 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 digits | |
| private 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 name | |
| private 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 function | |
| private 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 function | |
| private 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", "Ñ‘")); | |
| } | |
| //undocumented | |
| template 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; | |
| } | |
| } | |
| assert(safeOp!"!="(Foo(1), Foo(2))); | |
| } | |
| /** | |
| Predicate that returns $(D_PARAM a < b). | |
| Correctly compares signed and unsigned integers, ie. -1 < 2U. | |
| */ | |
| alias lessThan = safeOp!"<"; | |
| /// | |
| pure @safe @nogc nothrow unittest | |
| { | |
| assert(lessThan(2, 3)); | |
| assert(lessThan(2U, 3U)); | |
| assert(lessThan(2, 3.0)); | |
| assert(lessThan(-2, 3U)); | |
| assert(lessThan(2, 3U)); | |
| assert(!lessThan(3U, -2)); | |
| assert(!lessThan(3U, 2)); | |
| assert(!lessThan(0, 0)); | |
| assert(!lessThan(0U, 0)); | |
| assert(!lessThan(0, 0U)); | |
| } | |
| /** | |
| Predicate that returns $(D_PARAM a > b). | |
| Correctly compares signed and unsigned integers, ie. 2U > -1. | |
| */ | |
| alias greaterThan = safeOp!">"; | |
| /// | |
| @safe unittest | |
| { | |
| assert(!greaterThan(2, 3)); | |
| assert(!greaterThan(2U, 3U)); | |
| assert(!greaterThan(2, 3.0)); | |
| assert(!greaterThan(-2, 3U)); | |
| assert(!greaterThan(2, 3U)); | |
| assert(greaterThan(3U, -2)); | |
| assert(greaterThan(3U, 2)); | |
| assert(!greaterThan(0, 0)); | |
| assert(!greaterThan(0U, 0)); | |
| assert(!greaterThan(0, 0U)); | |
| } | |
| /** | |
| Predicate that returns $(D_PARAM a == b). | |
| Correctly compares signed and unsigned integers, ie. !(-1 == ~0U). | |
| */ | |
| alias equalTo = safeOp!"=="; | |
| /// | |
| @safe unittest | |
| { | |
| assert(equalTo(0U, 0)); | |
| assert(equalTo(0, 0U)); | |
| assert(!equalTo(-1, ~0U)); | |
| } | |
| /** | |
| N-ary predicate that reverses the order of arguments, e.g., given | |
| $(D pred(a, b, c)), returns $(D pred(c, b, a)). | |
| Params: | |
| pred = A callable | |
| Returns: | |
| A function which calls `pred` after reversing the given parameters | |
| */ | |
| template reverseArgs(alias pred) | |
| { | |
| auto reverseArgs(Args...)(auto ref Args args) | |
| if (is(typeof(pred(Reverse!args)))) | |
| { | |
| return pred(Reverse!args); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| alias gt = reverseArgs!(binaryFun!("a < b")); | |
| assert(gt(2, 1) && !gt(1, 1)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int x = 42; | |
| bool xyz(int a, int b) { return a * x < b / x; } | |
| auto foo = &xyz; | |
| foo(4, 5); | |
| alias zyx = reverseArgs!(foo); | |
| assert(zyx(5, 4) == foo(4, 5)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| alias gt = reverseArgs!(binaryFun!("a < b")); | |
| assert(gt(2, 1) && !gt(1, 1)); | |
| int x = 42; | |
| bool xyz(int a, int b) { return a * x < b / x; } | |
| auto foo = &xyz; | |
| foo(4, 5); | |
| alias zyx = reverseArgs!(foo); | |
| assert(zyx(5, 4) == foo(4, 5)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int abc(int a, int b, int c) { return a * b + c; } | |
| alias cba = reverseArgs!abc; | |
| assert(abc(91, 17, 32) == cba(32, 17, 91)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int a(int a) { return a * 2; } | |
| alias _a = reverseArgs!a; | |
| assert(a(2) == _a(2)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int b() { return 4; } | |
| alias _b = reverseArgs!b; | |
| assert(b() == _b()); | |
| } | |
| /** | |
| Negates predicate `pred`. | |
| Params: | |
| pred = A string or a callable | |
| Returns: | |
| A function which calls `pred` and returns the logical negation of its | |
| return value. | |
| */ | |
| template not(alias pred) | |
| { | |
| auto not(T...)(auto ref T args) | |
| { | |
| static if (is(typeof(!pred(args)))) | |
| return !pred(args); | |
| else static if (T.length == 1) | |
| return !unaryFun!pred(args); | |
| else static if (T.length == 2) | |
| return !binaryFun!pred(args); | |
| else | |
| static assert(0); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.algorithm.searching : find; | |
| import std.uni : isWhite; | |
| string a = " Hello, world!"; | |
| assert(find!(not!isWhite)(a) == "Hello, world!"); | |
| } | |
| @safe unittest | |
| { | |
| assert(not!"a != 5"(5)); | |
| assert(not!"a != b"(5, 5)); | |
| assert(not!(() => false)()); | |
| assert(not!(a => a != 5)(5)); | |
| assert(not!((a, b) => a != b)(5, 5)); | |
| assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5)); | |
| } | |
| /** | |
| $(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially | |
| applies) $(D_PARAM fun) by tying its first argument to $(D_PARAM arg). | |
| Params: | |
| fun = A callable | |
| arg = The first argument to apply to `fun` | |
| Returns: | |
| A new function which calls `fun` with `arg` plus the passed parameters. | |
| */ | |
| template partial(alias fun, alias arg) | |
| { | |
| import std.traits : isCallable; | |
| // Check whether fun is a user defined type which implements opCall or a template. | |
| // As opCall itself can be templated, std.traits.isCallable does not work here. | |
| enum isSomeFunctor = (is(typeof(fun) == struct) || is(typeof(fun) == class)) && __traits(hasMember, fun, "opCall"); | |
| static if (isSomeFunctor || __traits(isTemplate, fun)) | |
| { | |
| auto partial(Ts...)(Ts args2) | |
| { | |
| static if (is(typeof(fun(arg, args2)))) | |
| { | |
| return fun(arg, args2); | |
| } | |
| else | |
| { | |
| static string errormsg() | |
| { | |
| string msg = "Cannot call '" ~ fun.stringof ~ "' with arguments " ~ | |
| "(" ~ arg.stringof; | |
| foreach (T; Ts) | |
| msg ~= ", " ~ T.stringof; | |
| msg ~= ")."; | |
| return msg; | |
| } | |
| static assert(0, errormsg()); | |
| } | |
| } | |
| } | |
| else static if (!isCallable!fun) | |
| { | |
| static assert(false, "Cannot apply partial to a non-callable '" ~ fun.stringof ~ "'."); | |
| } | |
| else | |
| { | |
| import std.meta : Filter; | |
| static if (__traits(compiles, __traits(getOverloads, | |
| __traits(parent, fun), __traits(identifier, fun)))) | |
| alias overloads = __traits(getOverloads, __traits(parent, fun), | |
| __traits(identifier, fun)); | |
| else | |
| alias overloads = AliasSeq!(fun); | |
| enum isCallableWithArg(alias fun) = Parameters!fun.length > 0 && | |
| is(typeof(arg) : Parameters!fun[0]); | |
| alias candidates = Filter!(isCallableWithArg, overloads); | |
| static if (overloads.length == 1 && Parameters!fun.length == 0) | |
| { | |
| static assert(0, "Cannot partially apply '" ~ fun.stringof ~ "'." ~ | |
| "'" ~ fun.stringof ~ "' has 0 arguments."); | |
| } | |
| else static if (candidates.length == 0) | |
| { | |
| import std.meta : NoDuplicates, staticMap; | |
| enum hasParameters(alias fun) = Parameters!fun.length > 0; | |
| alias firstParameter(alias fun) = Parameters!fun[0]; | |
| alias firstParameters = NoDuplicates!( | |
| staticMap!(firstParameter, Filter!(hasParameters, overloads))); | |
| string errorMsg() | |
| { | |
| string msg = "Argument mismatch for '" ~ fun.stringof ~ | |
| "': expected " ~ firstParameters[0].stringof; | |
| static foreach (firstParam; firstParameters[1 .. $]) | |
| msg ~= " or " ~ firstParam.stringof; | |
| msg ~= ", but got " ~ typeof(arg).stringof ~ "."; | |
| return msg; | |
| } | |
| static assert(0, errorMsg()); | |
| } | |
| else | |
| { | |
| import std.traits : ReturnType; | |
| static foreach (candidate; candidates) | |
| ReturnType!candidate partial(Parameters!candidate[1..$] args2) | |
| { | |
| return candidate(arg, args2); | |
| } | |
| } | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int fun(int a, int b) { return a + b; } | |
| alias fun5 = partial!(fun, 5); | |
| assert(fun5(6) == 11); | |
| // Note that in most cases you'd use an alias instead of a value | |
| // assignment. Using an alias allows you to partially evaluate template | |
| // functions without committing to a particular type of the function. | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=21457 | |
| /// | |
| @safe unittest | |
| { | |
| // Overloads are resolved when the partially applied function is called | |
| // with the remaining arguments. | |
| struct S | |
| { | |
| static char fun(int i, string s) { return s[i]; } | |
| static int fun(int a, int b) { return a * b; } | |
| } | |
| alias fun3 = partial!(S.fun, 3); | |
| assert(fun3("hello") == 'l'); | |
| assert(fun3(10) == 30); | |
| } | |
| // tests for partially evaluating callables | |
| @safe unittest | |
| { | |
| static int f1(int a, int b) { return a + b; } | |
| assert(partial!(f1, 5)(6) == 11); | |
| int f2(int a, int b) { return a + b; } | |
| int x = 5; | |
| assert(partial!(f2, x)(6) == 11); | |
| x = 7; | |
| assert(partial!(f2, x)(6) == 13); | |
| static assert(partial!(f2, 5)(6) == 11); | |
| auto dg = &f2; | |
| auto f3 = &partial!(dg, x); | |
| assert(f3(6) == 13); | |
| static int funOneArg(int a) { return a; } | |
| assert(partial!(funOneArg, 1)() == 1); | |
| static int funThreeArgs(int a, int b, int c) { return a + b + c; } | |
| alias funThreeArgs1 = partial!(funThreeArgs, 1); | |
| assert(funThreeArgs1(2, 3) == 6); | |
| static assert(!is(typeof(funThreeArgs1(2)))); | |
| enum xe = 5; | |
| alias fe = partial!(f2, xe); | |
| static assert(fe(6) == 11); | |
| } | |
| // tests for partially evaluating templated/overloaded callables | |
| @safe unittest | |
| { | |
| static auto add(A, B)(A x, B y) | |
| { | |
| return x + y; | |
| } | |
| alias add5 = partial!(add, 5); | |
| assert(add5(6) == 11); | |
| static assert(!is(typeof(add5()))); | |
| static assert(!is(typeof(add5(6, 7)))); | |
| // taking address of templated partial evaluation needs explicit type | |
| auto dg = &add5!(int); | |
| assert(dg(6) == 11); | |
| int x = 5; | |
| alias addX = partial!(add, x); | |
| assert(addX(6) == 11); | |
| static struct Callable | |
| { | |
| static string opCall(string a, string b) { return a ~ b; } | |
| int opCall(int a, int b) { return a * b; } | |
| double opCall(double a, double b) { return a + b; } | |
| } | |
| Callable callable; | |
| assert(partial!(Callable, "5")("6") == "56"); | |
| assert(partial!(callable, 5)(6) == 30); | |
| assert(partial!(callable, 7.0)(3.0) == 7.0 + 3.0); | |
| static struct TCallable | |
| { | |
| auto opCall(A, B)(A a, B b) | |
| { | |
| return a + b; | |
| } | |
| } | |
| TCallable tcallable; | |
| assert(partial!(tcallable, 5)(6) == 11); | |
| static assert(!is(typeof(partial!(tcallable, "5")(6)))); | |
| static struct NonCallable{} | |
| static assert(!__traits(compiles, partial!(NonCallable, 5)), "Partial should not work on non-callable structs."); | |
| static assert(!__traits(compiles, partial!(NonCallable.init, 5)), | |
| "Partial should not work on instances of non-callable structs."); | |
| static A funOneArg(A)(A a) { return a; } | |
| alias funOneArg1 = partial!(funOneArg, 1); | |
| assert(funOneArg1() == 1); | |
| static auto funThreeArgs(A, B, C)(A a, B b, C c) { return a + b + c; } | |
| alias funThreeArgs1 = partial!(funThreeArgs, 1); | |
| assert(funThreeArgs1(2, 3) == 6); | |
| static assert(!is(typeof(funThreeArgs1(1)))); | |
| auto dg2 = &funOneArg1!(); | |
| assert(dg2() == 1); | |
| } | |
| // Fix https://issues.dlang.org/show_bug.cgi?id=15732 | |
| @safe unittest | |
| { | |
| // Test whether it works with functions. | |
| auto partialFunction(){ | |
| auto fullFunction = (float a, float b, float c) => a + b / c; | |
| alias apply1 = partial!(fullFunction, 1); | |
| return &apply1; | |
| } | |
| auto result = partialFunction()(2, 4); | |
| assert(result == 1.5f); | |
| // And with delegates. | |
| auto partialDelegate(float c){ | |
| auto fullDelegate = (float a, float b) => a + b / c; | |
| alias apply1 = partial!(fullDelegate, 1); | |
| return &apply1; | |
| } | |
| auto result2 = partialDelegate(4)(2); | |
| assert(result2 == 1.5f); | |
| } | |
| /** | |
| Takes a function of (potentially) many arguments, and returns a function taking | |
| one argument and returns a callable taking the rest. `f(x, y) == curry(f)(x)(y)` | |
| Params: | |
| F = a function taking at least one argument | |
| t = a callable object whose opCall takes at least 1 object | |
| Returns: | |
| A single parameter callable object | |
| */ | |
| template curry(alias F) | |
| if (isCallable!F && Parameters!F.length) | |
| { | |
| //inspired from the implementation from Artur Skawina here: | |
| //https://forum.dlang.org/post/mailman.1626.1340110492.24740.digitalmars-d@puremagic.com | |
| //This implementation stores a copy of all filled in arguments with each curried result | |
| //this way, the curried functions are independent and don't share any references | |
| //eg: auto fc = curry!f; auto fc1 = fc(1); auto fc2 = fc(2); fc1(3) != fc2(3) | |
| struct CurryImpl(size_t N) | |
| { | |
| alias FParams = Parameters!F; | |
| FParams[0 .. N] storedArguments; | |
| static if (N > 0) | |
| { | |
| this(U : FParams[N - 1])(ref CurryImpl!(N - 1) prev, ref U arg) | |
| { | |
| storedArguments[0 .. N - 1] = prev.storedArguments[]; | |
| storedArguments[N-1] = arg; | |
| } | |
| } | |
| auto opCall(U : FParams[N])(auto ref U arg) return scope | |
| { | |
| static if (N == FParams.length - 1) | |
| { | |
| return F(storedArguments, arg); | |
| } | |
| else | |
| { | |
| return CurryImpl!(N + 1)(this, arg); | |
| } | |
| } | |
| } | |
| auto curry() | |
| { | |
| CurryImpl!0 res; | |
| return res; // return CurryImpl!0.init segfaults for delegates on Windows | |
| } | |
| } | |
| /// | |
| pure @safe @nogc nothrow unittest | |
| { | |
| int f(int x, int y, int z) | |
| { | |
| return x + y + z; | |
| } | |
| auto cf = curry!f; | |
| auto cf1 = cf(1); | |
| auto cf2 = cf(2); | |
| assert(cf1(2)(3) == f(1, 2, 3)); | |
| assert(cf2(2)(3) == f(2, 2, 3)); | |
| } | |
| ///ditto | |
| auto curry(T)(T t) | |
| if (isCallable!T && Parameters!T.length) | |
| { | |
| static auto fun(ref T inst, ref Parameters!T args) | |
| { | |
| return inst(args); | |
| } | |
| return curry!fun()(t); | |
| } | |
| /// | |
| pure @safe @nogc nothrow unittest | |
| { | |
| //works with callable structs too | |
| struct S | |
| { | |
| int w; | |
| int opCall(int x, int y, int z) | |
| { | |
| return w + x + y + z; | |
| } | |
| } | |
| S s; | |
| s.w = 5; | |
| auto cs = curry(s); | |
| auto cs1 = cs(1); | |
| auto cs2 = cs(2); | |
| assert(cs1(2)(3) == s(1, 2, 3)); | |
| assert(cs1(2)(3) == (1 + 2 + 3 + 5)); | |
| assert(cs2(2)(3) ==s(2, 2, 3)); | |
| } | |
| @safe pure @nogc nothrow unittest | |
| { | |
| //currying a single argument function does nothing | |
| int pork(int a){ return a*2;} | |
| auto curryPork = curry!pork; | |
| assert(curryPork(0) == pork(0)); | |
| assert(curryPork(1) == pork(1)); | |
| assert(curryPork(-1) == pork(-1)); | |
| assert(curryPork(1000) == pork(1000)); | |
| //test 2 argument function | |
| double mixedVeggies(double a, int b, bool) | |
| { | |
| return a + b; | |
| } | |
| auto mixedCurry = curry!mixedVeggies; | |
| assert(mixedCurry(10)(20)(false) == mixedVeggies(10, 20, false)); | |
| assert(mixedCurry(100)(200)(true) == mixedVeggies(100, 200, true)); | |
| // struct with opCall | |
| struct S | |
| { | |
| double opCall(int x, double y, short z) const pure nothrow @nogc | |
| { | |
| return x*y*z; | |
| } | |
| } | |
| S s; | |
| auto curriedStruct = curry(s); | |
| assert(curriedStruct(1)(2)(short(3)) == s(1, 2, short(3))); | |
| assert(curriedStruct(300)(20)(short(10)) == s(300, 20, short(10))); | |
| } | |
| pure @safe nothrow unittest | |
| { | |
| auto cfl = curry!((double a, int b) => a + b); | |
| assert(cfl(13)(2) == 15); | |
| int c = 42; | |
| auto cdg = curry!((double a, int b) => a + b + c); | |
| assert(cdg(13)(2) == 57); | |
| static class C | |
| { | |
| int opCall(int mult, int add) pure @safe nothrow @nogc scope | |
| { | |
| return mult * 42 + add; | |
| } | |
| } | |
| scope C ci = new C(); | |
| scope cc = curry(ci); | |
| assert(cc(2)(4) == ci(2, 4)); | |
| } | |
| // Disallows callables without parameters | |
| pure @safe @nogc nothrow unittest | |
| { | |
| static void noargs() {} | |
| static assert(!__traits(compiles, curry!noargs())); | |
| static struct NoArgs | |
| { | |
| void opCall() {} | |
| } | |
| static assert(!__traits(compiles, curry(NoArgs.init))); | |
| } | |
| private template Iota(size_t n) | |
| { | |
| static if (n == 0) | |
| alias Iota = AliasSeq!(); | |
| else | |
| alias Iota = AliasSeq!(Iota!(n - 1), n - 1); | |
| } | |
| /** | |
| Takes multiple functions and adjoins them together. | |
| Params: | |
| F = the call-able(s) to adjoin | |
| Returns: | |
| A new function which returns a $(REF Tuple, std,typecons). Each of the | |
| elements of the tuple will be the return values of `F`. | |
| Note: In the special case where only a single function is provided | |
| ($(D F.length == 1)), adjoin simply aliases to the single passed function | |
| (`F[0]`). | |
| */ | |
| template adjoin(F...) | |
| if (F.length >= 1) | |
| { | |
| static if (F.length == 1) | |
| alias adjoin = F[0]; | |
| else | |
| auto adjoin(V...)(auto ref V a) | |
| { | |
| import std.typecons : tuple; | |
| import std.meta : staticMap; | |
| auto resultElement(size_t i)() | |
| { | |
| return F[i](a); | |
| } | |
| return tuple(staticMap!(resultElement, Iota!(F.length))); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.typecons : Tuple; | |
| static bool f1(int a) { return a != 0; } | |
| static int f2(int a) { return a / 2; } | |
| auto x = adjoin!(f1, f2)(5); | |
| assert(is(typeof(x) == Tuple!(bool, int))); | |
| assert(x[0] == true && x[1] == 2); | |
| } | |
| @safe unittest | |
| { | |
| import std.typecons : Tuple; | |
| static bool F1(int a) { return a != 0; } | |
| auto x1 = adjoin!(F1)(5); | |
| static int F2(int a) { return a / 2; } | |
| auto x2 = adjoin!(F1, F2)(5); | |
| assert(is(typeof(x2) == Tuple!(bool, int))); | |
| assert(x2[0] && x2[1] == 2); | |
| auto x3 = adjoin!(F1, F2, F2)(5); | |
| assert(is(typeof(x3) == Tuple!(bool, int, int))); | |
| assert(x3[0] && x3[1] == 2 && x3[2] == 2); | |
| bool F4(int a) { return a != x1; } | |
| alias eff4 = adjoin!(F4); | |
| static struct S | |
| { | |
| bool delegate(int) @safe store; | |
| int fun() { return 42 + store(5); } | |
| } | |
| S s; | |
| s.store = (int a) { return eff4(a); }; | |
| auto x4 = s.fun(); | |
| assert(x4 == 43); | |
| } | |
| @safe unittest | |
| { | |
| import std.meta : staticMap; | |
| import std.typecons : Tuple, tuple; | |
| alias funs = staticMap!(unaryFun, "a", "a * 2", "a * 3", "a * a", "-a"); | |
| alias afun = adjoin!funs; | |
| assert(afun(5) == tuple(5, 10, 15, 25, -5)); | |
| static class C{} | |
| alias IC = immutable(C); | |
| IC foo(){return typeof(return).init;} | |
| Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)(); | |
| static struct S{int* p;} | |
| alias IS = immutable(S); | |
| IS bar(){return typeof(return).init;} | |
| enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)(); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=21347 | |
| @safe @betterC unittest | |
| { | |
| alias f = (int n) => n + 1; | |
| alias g = (int n) => n + 2; | |
| alias h = (int n) => n + 3; | |
| alias i = (int n) => n + 4; | |
| auto result = adjoin!(f, g, h, i)(0); | |
| assert(result[0] == 1); | |
| assert(result[1] == 2); | |
| assert(result[2] == 3); | |
| assert(result[3] == 4); | |
| } | |
| /** | |
| Composes passed-in functions $(D fun[0], fun[1], ...). | |
| Params: | |
| fun = the call-able(s) or `string`(s) to compose into one function | |
| Returns: | |
| A new function `f(x)` that in turn returns `fun[0](fun[1](...(x)))...`. | |
| See_Also: $(LREF pipe) | |
| */ | |
| template compose(fun...) | |
| if (fun.length > 0) | |
| { | |
| static if (fun.length == 1) | |
| { | |
| alias compose = unaryFun!(fun[0]); | |
| } | |
| else | |
| { | |
| alias fun0 = unaryFun!(fun[0]); | |
| alias rest = compose!(fun[1 .. $]); | |
| auto compose(Args...)(Args args) | |
| { | |
| return fun0(rest(args)); | |
| } | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.algorithm.iteration : map; | |
| import std.array : split; | |
| import std.conv : to; | |
| // First split a string in whitespace-separated tokens and then | |
| // convert each token into an integer | |
| assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3])); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=6484 | |
| @safe unittest | |
| { | |
| int f(int a) { return a; } | |
| int g(int a) { return a; } | |
| int h(int a,int b,int c) { return a * b * c; } | |
| alias F = compose!(f,g,h); | |
| assert(F(1,2,3) == f(g(h(1,2,3)))); | |
| } | |
| /** | |
| Pipes functions in sequence. Offers the same functionality as $(D | |
| compose), but with functions specified in reverse order. This may | |
| lead to more readable code in some situation because the order of | |
| execution is the same as lexical order. | |
| Params: | |
| fun = the call-able(s) or `string`(s) to compose into one function | |
| Returns: | |
| A new function `f(x)` that in turn returns `fun[$-1](...fun[1](fun[0](x)))...`. | |
| Example: | |
| ---- | |
| // Read an entire text file, split the resulting string in | |
| // whitespace-separated tokens, and then convert each token into an | |
| // integer | |
| int[] a = pipe!(readText, split, map!(to!(int)))("file.txt"); | |
| ---- | |
| See_Also: $(LREF compose) | |
| */ | |
| alias pipe(fun...) = compose!(Reverse!(fun)); | |
| /// | |
| @safe unittest | |
| { | |
| import std.conv : to; | |
| string foo(int a) { return to!(string)(a); } | |
| int bar(string a) { return to!(int)(a) + 1; } | |
| double baz(int a) { return a + 0.5; } | |
| assert(compose!(baz, bar, foo)(1) == 2.5); | |
| assert(pipe!(foo, bar, baz)(1) == 2.5); | |
| assert(compose!(baz, `to!(int)(a) + 1`, foo)(1) == 2.5); | |
| assert(compose!(baz, bar)("1"[]) == 2.5); | |
| assert(compose!(baz, bar)("1") == 2.5); | |
| assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5); | |
| } | |
| private template getOverloads(alias fun) | |
| { | |
| import std.meta : AliasSeq; | |
| static if (__traits(compiles, __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true))) | |
| alias getOverloads = __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true); | |
| else | |
| alias getOverloads = AliasSeq!fun; | |
| } | |
| /** | |
| * $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as | |
| * to avoid repeated computation. The memoization structure is a hash table keyed by a | |
| * tuple of the function's arguments. There is a speed gain if the | |
| * function is repeatedly called with the same arguments and is more | |
| * expensive than a hash table lookup. For more information on memoization, refer to $(HTTP docs.google.com/viewer?url=http%3A%2F%2Fhop.perl.plover.com%2Fbook%2Fpdf%2F03CachingAndMemoization.pdf, this book chapter). | |
| Example: | |
| ---- | |
| double transmogrify(int a, string b) | |
| { | |
| ... expensive computation ... | |
| } | |
| alias fastTransmogrify = memoize!transmogrify; | |
| unittest | |
| { | |
| auto slow = transmogrify(2, "hello"); | |
| auto fast = fastTransmogrify(2, "hello"); | |
| assert(slow == fast); | |
| } | |
| ---- | |
| Params: | |
| fun = the call-able to memozie | |
| maxSize = The maximum size of the GC buffer to hold the return values | |
| Returns: | |
| A new function which calls `fun` and caches its return values. | |
| Note: | |
| Technically the memoized function should be pure because `memoize` assumes it will | |
| always return the same result for a given tuple of arguments. However, `memoize` does not | |
| enforce that because sometimes it is useful to memoize an impure function, too. | |
| */ | |
| template memoize(alias fun) | |
| { | |
| import std.traits : Parameters; | |
| import std.meta : anySatisfy; | |
| // Specific overloads: | |
| alias overloads = getOverloads!fun; | |
| static foreach (fn; overloads) | |
| static if (is(Parameters!fn)) | |
| alias memoize = impl!(Parameters!fn); | |
| enum isTemplate(alias a) = __traits(isTemplate, a); | |
| static if (anySatisfy!(isTemplate, overloads)) | |
| { | |
| // Generic implementation | |
| alias memoize = impl; | |
| } | |
| auto impl(Args...)(Args args) | |
| if (is(typeof(fun(args)))) | |
| { | |
| import std.typecons : Tuple, tuple; | |
| import std.traits : Unqual; | |
| static if (args.length > 0) | |
| { | |
| static Unqual!(typeof(fun(args)))[Tuple!(typeof(args))] memo; | |
| auto t = Tuple!Args(args); | |
| if (auto p = t in memo) | |
| return *p; | |
| auto r = fun(args); | |
| memo[t] = r; | |
| return r; | |
| } | |
| else | |
| { | |
| static typeof(fun(args)) result; | |
| result = fun(args); | |
| return result; | |
| } | |
| } | |
| } | |
| /// ditto | |
| template memoize(alias fun, uint maxSize) | |
| { | |
| import std.traits : Parameters; | |
| import std.meta : anySatisfy; | |
| // Specific overloads: | |
| alias overloads = getOverloads!fun; | |
| static foreach (fn; overloads) | |
| static if (is(Parameters!fn)) | |
| alias memoize = impl!(Parameters!fn); | |
| enum isTemplate(alias a) = __traits(isTemplate, a); | |
| static if (anySatisfy!(isTemplate, overloads)) | |
| { | |
| // Generic implementation | |
| alias memoize = impl; | |
| } | |
| auto impl(Args...)(Args args) | |
| if (is(typeof(fun(args)))) | |
| { | |
| static if (args.length > 0) | |
| { | |
| import std.meta : staticMap; | |
| import std.traits : hasIndirections, Unqual; | |
| import std.typecons : tuple; | |
| alias returnType = typeof(fun(args)); | |
| static struct Value { staticMap!(Unqual, Args) args; Unqual!returnType res; } | |
| static Value[] memo; | |
| static size_t[] initialized; | |
| if (!memo.length) | |
| { | |
| import core.memory : GC; | |
| // Ensure no allocation overflows | |
| static assert(maxSize < size_t.max / Value.sizeof); | |
| static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1)); | |
| enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN); | |
| memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize]; | |
| enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); | |
| initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords]; | |
| } | |
| import core.bitop : bt, bts; | |
| import core.lifetime : emplace; | |
| size_t hash; | |
| foreach (ref arg; args) | |
| hash = hashOf(arg, hash); | |
| // cuckoo hashing | |
| immutable idx1 = hash % maxSize; | |
| if (!bt(initialized.ptr, idx1)) | |
| { | |
| emplace(&memo[idx1], args, fun(args)); | |
| // only set to initialized after setting args and value | |
| // https://issues.dlang.org/show_bug.cgi?id=14025 | |
| bts(initialized.ptr, idx1); | |
| return memo[idx1].res; | |
| } | |
| else if (memo[idx1].args == args) | |
| return memo[idx1].res; | |
| // FNV prime | |
| immutable idx2 = (hash * 16_777_619) % maxSize; | |
| if (!bt(initialized.ptr, idx2)) | |
| { | |
| emplace(&memo[idx2], memo[idx1]); | |
| bts(initialized.ptr, idx2); | |
| } | |
| else if (memo[idx2].args == args) | |
| return memo[idx2].res; | |
| else if (idx1 != idx2) | |
| memo[idx2] = memo[idx1]; | |
| memo[idx1] = Value(args, fun(args)); | |
| return memo[idx1].res; | |
| } | |
| else | |
| { | |
| static typeof(fun(args)) result; | |
| result = fun(args); | |
| return result; | |
| } | |
| } | |
| } | |
| /** | |
| * To _memoize a recursive function, simply insert the memoized call in lieu of the plain recursive call. | |
| * For example, to transform the exponential-time Fibonacci implementation into a linear-time computation: | |
| */ | |
| @safe nothrow | |
| unittest | |
| { | |
| ulong fib(ulong n) @safe nothrow | |
| { | |
| return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1); | |
| } | |
| assert(fib(10) == 55); | |
| } | |
| /** | |
| * To improve the speed of the factorial function, | |
| */ | |
| @safe unittest | |
| { | |
| ulong fact(ulong n) @safe | |
| { | |
| return n < 2 ? 1 : n * memoize!fact(n - 1); | |
| } | |
| assert(fact(10) == 3628800); | |
| } | |
| /** | |
| * This memoizes all values of `fact` up to the largest argument. To only cache the final | |
| * result, move `memoize` outside the function as shown below. | |
| */ | |
| @safe unittest | |
| { | |
| ulong factImpl(ulong n) @safe | |
| { | |
| return n < 2 ? 1 : n * factImpl(n - 1); | |
| } | |
| alias fact = memoize!factImpl; | |
| assert(fact(10) == 3628800); | |
| } | |
| /** | |
| * When the `maxSize` parameter is specified, memoize will used | |
| * a fixed size hash table to limit the number of cached entries. | |
| */ | |
| @system unittest // not @safe due to memoize | |
| { | |
| ulong fact(ulong n) | |
| { | |
| // Memoize no more than 8 values | |
| return n < 2 ? 1 : n * memoize!(fact, 8)(n - 1); | |
| } | |
| assert(fact(8) == 40320); | |
| // using more entries than maxSize will overwrite existing entries | |
| assert(fact(10) == 3628800); | |
| } | |
| // Issue 20099 | |
| @system unittest // not @safe due to memoize | |
| { | |
| int i = 3; | |
| alias a = memoize!((n) => i + n); | |
| alias b = memoize!((n) => i + n, 3); | |
| assert(a(3) == 6); | |
| assert(b(3) == 6); | |
| } | |
| @system unittest // not @safe due to memoize | |
| { | |
| static Object objNum(int a) { return new Object(); } | |
| assert(memoize!objNum(0) is memoize!objNum(0U)); | |
| assert(memoize!(objNum, 3)(0) is memoize!(objNum, 3)(0U)); | |
| } | |
| @system unittest // not @safe due to memoize | |
| { | |
| struct S | |
| { | |
| static int fun() { return 0; } | |
| static int fun(int i) { return 1; } | |
| } | |
| assert(memoize!(S.fun)() == 0); | |
| assert(memoize!(S.fun)(3) == 1); | |
| assert(memoize!(S.fun, 3)() == 0); | |
| assert(memoize!(S.fun, 3)(3) == 1); | |
| } | |
| @system unittest // not @safe due to memoize | |
| { | |
| import core.math : sqrt; | |
| alias msqrt = memoize!(function double(double x) { return sqrt(x); }); | |
| auto y = msqrt(2.0); | |
| assert(y == msqrt(2.0)); | |
| y = msqrt(4.0); | |
| assert(y == sqrt(4.0)); | |
| // alias mrgb2cmyk = memoize!rgb2cmyk; | |
| // auto z = mrgb2cmyk([43, 56, 76]); | |
| // assert(z == mrgb2cmyk([43, 56, 76])); | |
| //alias mfib = memoize!fib; | |
| static ulong fib(ulong n) @safe | |
| { | |
| alias mfib = memoize!fib; | |
| return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1); | |
| } | |
| auto z = fib(10); | |
| assert(z == 89); | |
| static ulong fact(ulong n) @safe | |
| { | |
| alias mfact = memoize!fact; | |
| return n < 2 ? 1 : n * mfact(n - 1); | |
| } | |
| assert(fact(10) == 3628800); | |
| // https://issues.dlang.org/show_bug.cgi?id=12568 | |
| static uint len2(const string s) { // Error | |
| alias mLen2 = memoize!len2; | |
| if (s.length == 0) | |
| return 0; | |
| else | |
| return 1 + mLen2(s[1 .. $]); | |
| } | |
| int _func(int x) @safe { return 1; } | |
| alias func = memoize!(_func, 10); | |
| assert(func(int.init) == 1); | |
| assert(func(int.init) == 1); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=16079 | |
| // memoize should work with arrays | |
| @system unittest // not @safe with -dip1000 due to memoize | |
| { | |
| int executed = 0; | |
| T median(T)(const T[] nums) { | |
| import std.algorithm.sorting : sort; | |
| executed++; | |
| auto arr = nums.dup; | |
| arr.sort(); | |
| if (arr.length % 2) | |
| return arr[$ / 2]; | |
| else | |
| return (arr[$ / 2 - 1] | |
| + arr[$ / 2]) / 2; | |
| } | |
| alias fastMedian = memoize!(median!int); | |
| assert(fastMedian([7, 5, 3]) == 5); | |
| assert(fastMedian([7, 5, 3]) == 5); | |
| assert(executed == 1); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with structs | |
| @safe unittest | |
| { | |
| int executed = 0; | |
| T pickFirst(T)(T first) | |
| { | |
| executed++; | |
| return first; | |
| } | |
| struct Foo { int k; } | |
| Foo A = Foo(3); | |
| alias first = memoize!(pickFirst!Foo); | |
| assert(first(Foo(3)) == A); | |
| assert(first(Foo(3)) == A); | |
| assert(executed == 1); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=20439 memoize should work with void opAssign | |
| @safe unittest | |
| { | |
| static struct S | |
| { | |
| void opAssign(S) {} | |
| } | |
| assert(memoize!(() => S()) == S()); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with classes | |
| @system unittest // not @safe with -dip1000 due to memoize | |
| { | |
| int executed = 0; | |
| T pickFirst(T)(T first) | |
| { | |
| executed++; | |
| return first; | |
| } | |
| class Bar | |
| { | |
| size_t k; | |
| this(size_t k) | |
| { | |
| this.k = k; | |
| } | |
| override size_t toHash() | |
| { | |
| return k; | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| auto b = cast(Bar) o; | |
| return b && k == b.k; | |
| } | |
| } | |
| alias firstClass = memoize!(pickFirst!Bar); | |
| assert(firstClass(new Bar(3)).k == 3); | |
| assert(firstClass(new Bar(3)).k == 3); | |
| assert(executed == 1); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=20302 | |
| @system unittest | |
| { | |
| version (none) // TODO change `none` to `all` and fix remaining limitations | |
| struct S { const int len; } | |
| else | |
| struct S { int len; } | |
| static string fun000( string str, S s) { return str[0 .. s.len] ~ "123"; } | |
| static string fun001( string str, const S s) { return str[0 .. s.len] ~ "123"; } | |
| static string fun010(const string str, S s) { return str[0 .. s.len] ~ "123"; } | |
| static string fun011(const string str, const S s) { return str[0 .. s.len] ~ "123"; } | |
| static const(string) fun100( string str, S s) { return str[0 .. s.len] ~ "123"; } | |
| static const(string) fun101( string str, const S s) { return str[0 .. s.len] ~ "123"; } | |
| static const(string) fun110(const string str, S s) { return str[0 .. s.len] ~ "123"; } | |
| static const(string) fun111(const string str, const S s) { return str[0 .. s.len] ~ "123"; } | |
| static foreach (fun; AliasSeq!(fun000, fun001, fun010, fun011, fun100, fun101, fun110, fun111)) | |
| {{ | |
| alias mfun = memoize!fun; | |
| assert(mfun("abcdefgh", S(3)) == "abc123"); | |
| alias mfun2 = memoize!(fun, 42); | |
| assert(mfun2("asd", S(3)) == "asd123"); | |
| }} | |
| } | |
| // memoize should continue to work with functions that cannot be evaluated at compile time | |
| @system unittest | |
| { | |
| __gshared string[string] glob; | |
| static bool foo() | |
| { | |
| return (":-)" in glob) is null; | |
| } | |
| assert(memoize!foo); | |
| } | |
| private struct DelegateFaker(F) | |
| { | |
| import std.typecons : FuncInfo, MemberFunctionGenerator; | |
| // for @safe | |
| static F castToF(THIS)(THIS x) @trusted | |
| { | |
| return cast(F) x; | |
| } | |
| /* | |
| * What all the stuff below does is this: | |
| *-------------------- | |
| * struct DelegateFaker(F) { | |
| * extern(linkage) | |
| * [ref] ReturnType!F doIt(Parameters!F args) [@attributes] | |
| * { | |
| * auto fp = cast(F) &this; | |
| * return fp(args); | |
| * } | |
| * } | |
| *-------------------- | |
| */ | |
| // We will use MemberFunctionGenerator in std.typecons. This is a policy | |
| // configuration for generating the doIt(). | |
| template GeneratingPolicy() | |
| { | |
| // Inform the genereator that we only have type information. | |
| enum WITHOUT_SYMBOL = true; | |
| // Generate the function body of doIt(). | |
| template generateFunctionBody(unused...) | |
| { | |
| enum generateFunctionBody = | |
| // [ref] ReturnType doIt(Parameters args) @attributes | |
| q{ | |
| // When this function gets called, the this pointer isn't | |
| // really a this pointer (no instance even really exists), but | |
| // a function pointer that points to the function to be called. | |
| // Cast it to the correct type and call it. | |
| auto fp = castToF(&this); | |
| return fp(args); | |
| }; | |
| } | |
| } | |
| // Type information used by the generated code. | |
| alias FuncInfo_doIt = FuncInfo!(F); | |
| // Generate the member function doIt(). | |
| mixin( MemberFunctionGenerator!(GeneratingPolicy!()) | |
| .generateFunction!("FuncInfo_doIt", "doIt", F) ); | |
| } | |
| /** | |
| * Convert a callable to a delegate with the same parameter list and | |
| * return type, avoiding heap allocations and use of auxiliary storage. | |
| * | |
| * Params: | |
| * fp = a function pointer or an aggregate type with `opCall` defined. | |
| * Returns: | |
| * A delegate with the context pointer pointing to nothing. | |
| * | |
| * Example: | |
| * ---- | |
| * void doStuff() { | |
| * writeln("Hello, world."); | |
| * } | |
| * | |
| * void runDelegate(void delegate() myDelegate) { | |
| * myDelegate(); | |
| * } | |
| * | |
| * auto delegateToPass = toDelegate(&doStuff); | |
| * runDelegate(delegateToPass); // Calls doStuff, prints "Hello, world." | |
| * ---- | |
| * | |
| * BUGS: | |
| * $(UL | |
| * $(LI Ignores C-style / D-style variadic arguments.) | |
| * ) | |
| */ | |
| auto toDelegate(F)(auto ref F fp) | |
| if (isCallable!(F)) | |
| { | |
| static if (is(F == delegate)) | |
| { | |
| return fp; | |
| } | |
| else static if (is(F Func == Func*) && is(Func == function)) | |
| { | |
| return function(ref F fp) @trusted | |
| { | |
| return buildDelegate(fp); | |
| }(fp); | |
| } | |
| else static if (is(typeof(&F.opCall) == delegate) | |
| || (is(typeof(&F.opCall) V : V*) && is(V == function))) | |
| { | |
| return toDelegate(&fp.opCall); | |
| } | |
| else static if (is(typeof(&fp.opCall!()))) | |
| { | |
| return toDelegate(&fp.opCall!()); | |
| } | |
| else | |
| { | |
| static assert(false, "Unsupported type of callable, please open an issue."); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static int inc(ref uint num) { | |
| num++; | |
| return 8675309; | |
| } | |
| uint myNum = 0; | |
| auto incMyNumDel = toDelegate(&inc); | |
| auto returnVal = incMyNumDel(myNum); | |
| assert(myNum == 1); | |
| } | |
| private template buildDelegate(F) | |
| { | |
| auto buildDelegate(auto ref F fp) { | |
| alias DelType = typeof(&(new DelegateFaker!(F)).doIt); | |
| static struct DelegateFields { | |
| union { | |
| DelType del; | |
| //pragma(msg, typeof(del)); | |
| struct { | |
| void* contextPtr; | |
| void* funcPtr; | |
| } | |
| } | |
| } | |
| // fp is stored in the returned delegate's context pointer. | |
| // The returned delegate's function pointer points to | |
| // DelegateFaker.doIt. | |
| DelegateFields df; | |
| df.contextPtr = cast(void*) fp; | |
| DelegateFaker!(F) dummy; | |
| auto dummyDel = &dummy.doIt; | |
| df.funcPtr = dummyDel.funcptr; | |
| return df.del; | |
| } | |
| } | |
| @safe unittest | |
| { | |
| static int inc(ref int num) { | |
| num++; | |
| return 8675309; | |
| } | |
| struct S1 | |
| { | |
| static int myNum = 0x1337; | |
| static int opCall() { inc(myNum); return myNum; } | |
| } | |
| S1 s1; | |
| auto getvals1 = toDelegate(s1); | |
| static assert(!is(typeof(&s1.opCall) == delegate)); | |
| static assert( is(typeof(toDelegate(s1)) == delegate)); | |
| assert(getvals1() == 0x1338); | |
| } | |
| @system unittest | |
| { | |
| static int inc(ref uint num) { | |
| num++; | |
| return 8675309; | |
| } | |
| uint myNum = 0; | |
| auto incMyNumDel = toDelegate(&inc); | |
| int delegate(ref uint) dg = incMyNumDel; | |
| auto returnVal = incMyNumDel(myNum); | |
| assert(myNum == 1); | |
| interface I { int opCall(); } | |
| class C: I { int opCall() { inc(myNum); return myNum;} } | |
| auto c = new C; | |
| auto i = cast(I) c; | |
| auto getvalc = toDelegate(c); | |
| assert(getvalc() == 2); | |
| auto getvali = toDelegate(i); | |
| assert(getvali() == 3); | |
| struct S1 { int opCall() { inc(myNum); return myNum; } } | |
| S1 s1; | |
| auto getvals1 = toDelegate(s1); | |
| static assert(is(typeof(&s1.opCall) == delegate)); | |
| static assert(is(typeof(getvals1) == delegate)); | |
| assert(&s1.opCall is getvals1); | |
| assert(getvals1() == 4); | |
| struct S2 { static int opCall() { return 123456; } } | |
| S2 s2; | |
| auto getvals2 = toDelegate(s2); | |
| static assert(!is(typeof(&S2.opCall) == delegate)); | |
| static assert( is(typeof(getvals2) == delegate)); | |
| assert(getvals2() == 123456); | |
| /* test for attributes */ | |
| { | |
| static int refvar = 0xDeadFace; | |
| static ref int func_ref() { return refvar; } | |
| static int func_pure() pure { return 1; } | |
| static int func_nothrow() nothrow { return 2; } | |
| static int func_property() @property { return 3; } | |
| static int func_safe() @safe { return 4; } | |
| static int func_trusted() @trusted { return 5; } | |
| static int func_system() @system { return 6; } | |
| static int func_pure_nothrow() pure nothrow { return 7; } | |
| static int func_pure_nothrow_safe() pure nothrow @safe { return 8; } | |
| auto dg_ref = toDelegate(&func_ref); | |
| int delegate() pure dg_pure = toDelegate(&func_pure); | |
| int delegate() nothrow dg_nothrow = toDelegate(&func_nothrow); | |
| int delegate() @property dg_property = toDelegate(&func_property); | |
| int delegate() @safe dg_safe = toDelegate(&func_safe); | |
| int delegate() @trusted dg_trusted = toDelegate(&func_trusted); | |
| int delegate() @system dg_system = toDelegate(&func_system); | |
| int delegate() pure nothrow dg_pure_nothrow = toDelegate(&func_pure_nothrow); | |
| int delegate() @safe pure nothrow dg_pure_nothrow_safe = toDelegate(&func_pure_nothrow_safe); | |
| //static assert(is(typeof(dg_ref) == ref int delegate())); // [BUG@DMD] | |
| assert(dg_ref() == refvar); | |
| assert(dg_pure() == 1); | |
| assert(dg_nothrow() == 2); | |
| assert(dg_property() == 3); | |
| assert(dg_safe() == 4); | |
| assert(dg_trusted() == 5); | |
| assert(dg_system() == 6); | |
| assert(dg_pure_nothrow() == 7); | |
| assert(dg_pure_nothrow_safe() == 8); | |
| } | |
| /* test for linkage */ | |
| { | |
| struct S | |
| { | |
| extern(C) static void xtrnC() {} | |
| extern(D) static void xtrnD() {} | |
| } | |
| auto dg_xtrnC = toDelegate(&S.xtrnC); | |
| auto dg_xtrnD = toDelegate(&S.xtrnD); | |
| static assert(! is(typeof(dg_xtrnC) == typeof(dg_xtrnD))); | |
| } | |
| } | |
| @safe unittest | |
| { | |
| static struct S1 { static void opCall()() { } } | |
| static struct S2 { static T opCall(T = int)(T x) {return x; } } | |
| S1 i1; | |
| const dg1 = toDelegate(i1); | |
| dg1(); | |
| S2 i2; | |
| assert(toDelegate(i2)(0xBED) == 0xBED); | |
| } | |
| @safe unittest | |
| { | |
| static void fun() @system pure nothrow @nogc | |
| { | |
| return; | |
| } | |
| auto fn = &fun; | |
| static assert( is(typeof(fn) == void function() @system pure nothrow @nogc)); | |
| static assert(!is(typeof(fn) == void function() @safe pure nothrow @nogc)); | |
| auto dg = fn.toDelegate(); | |
| static assert( is(typeof(dg) == void delegate() @system pure nothrow @nogc)); | |
| static assert(!is(typeof(dg) == void delegate() @safe pure nothrow @nogc)); | |
| } | |
| /** | |
| * Passes the fields of a struct as arguments to a function. | |
| * | |
| * Can be used with a $(LINK2 https://dlang.org/spec/expression.html#function_literals, | |
| * function literal) to give temporary names to the fields of a struct or | |
| * tuple. | |
| * | |
| * Params: | |
| * fun = Callable that the struct's fields will be passed to. | |
| * | |
| * Returns: | |
| * A function that accepts a single struct as an argument and passes its | |
| * fields to `fun` when called. | |
| */ | |
| template bind(alias fun) | |
| { | |
| /** | |
| * Params: | |
| * args = The struct or tuple whose fields will be used as arguments. | |
| * | |
| * Returns: `fun(args.tupleof)` | |
| */ | |
| auto ref bind(T)(auto ref T args) | |
| if (is(T == struct)) | |
| { | |
| import std.meta : Map = staticMap; | |
| import core.lifetime : move; | |
| // Forwards the i'th member of `args` | |
| // Needed because core.lifetime.forward doesn't work on struct members | |
| template forwardArg(size_t i) | |
| { | |
| static if (__traits(isRef, args) || !is(typeof(move(args.tupleof[i])))) | |
| enum forwardArg = "args.tupleof[" ~ toCtString!i ~ "], "; | |
| else | |
| enum forwardArg = "move(args.tupleof[" ~ toCtString!i ~ "]), "; | |
| } | |
| static if (args.tupleof.length == 0) | |
| enum argList = ""; | |
| else | |
| alias argList = Map!(forwardArg, Iota!(args.tupleof.length)); | |
| return mixin("fun(", argList, ")"); | |
| } | |
| } | |
| /// Giving names to tuple elements | |
| @safe unittest | |
| { | |
| import std.typecons : tuple; | |
| auto name = tuple("John", "Doe"); | |
| string full = name.bind!((first, last) => first ~ " " ~ last); | |
| assert(full == "John Doe"); | |
| } | |
| /// Passing struct fields to a function | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : min, max; | |
| struct Pair | |
| { | |
| int a; | |
| int b; | |
| } | |
| auto p = Pair(123, 456); | |
| assert(p.bind!min == 123); // min(p.a, p.b) | |
| assert(p.bind!max == 456); // max(p.a, p.b) | |
| } | |
| /// In a range pipeline | |
| @safe unittest | |
| { | |
| import std.algorithm.iteration : map, filter; | |
| import std.algorithm.comparison : equal; | |
| import std.typecons : tuple; | |
| auto ages = [ | |
| tuple("Alice", 35), | |
| tuple("Bob", 64), | |
| tuple("Carol", 21), | |
| tuple("David", 39), | |
| tuple("Eve", 50) | |
| ]; | |
| auto overForty = ages | |
| .filter!(bind!((name, age) => age > 40)) | |
| .map!(bind!((name, age) => name)); | |
| assert(overForty.equal(["Bob", "Eve"])); | |
| } | |
| // Zero arguments | |
| @safe unittest | |
| { | |
| struct Empty {} | |
| assert(Empty().bind!(() => 123) == 123); | |
| } | |
| // Non-copyable arguments | |
| @safe unittest | |
| { | |
| import std.typecons : tuple; | |
| static struct NoCopy | |
| { | |
| int n; | |
| @disable this(this); | |
| } | |
| static struct Pair | |
| { | |
| NoCopy a, b; | |
| } | |
| static auto fun(NoCopy a, NoCopy b) | |
| { | |
| return tuple(a.n, b.n); | |
| } | |
| auto expected = fun(NoCopy(1), NoCopy(2)); | |
| assert(Pair(NoCopy(1), NoCopy(2)).bind!fun == expected); | |
| } | |
| // ref arguments | |
| @safe unittest | |
| { | |
| import std.typecons : tuple; | |
| auto t = tuple(123, 456); | |
| t.bind!((ref int a, int b) { a = 789; b = 1011; }); | |
| assert(t[0] == 789); | |
| assert(t[1] == 456); | |
| } | |
| // auto ref arguments | |
| @safe unittest | |
| { | |
| import std.typecons : tuple; | |
| auto t = tuple(123); | |
| t.bind!((auto ref x) { | |
| static assert(__traits(isRef, x)); | |
| }); | |
| tuple(123).bind!((auto ref x) { | |
| static assert(!__traits(isRef, x)); | |
| }); | |
| } | |
| /** | |
| * Enforces the evaluation of an expression during compile-time. | |
| * | |
| * Computes the value of an expression during compilation (CTFE). | |
| * | |
| * This is useful for call chains in functional programming | |
| * where declaring an `enum` constant would require splitting | |
| * the pipeline. | |
| * | |
| * Params: | |
| * expr = expression to evaluate | |
| * See_also: | |
| * $(LINK https://dlang.org/spec/function.html#interpretation) | |
| */ | |
| enum ctEval(alias expr) = expr; | |
| /// | |
| @safe unittest | |
| { | |
| import std.math : abs; | |
| // No explicit `enum` needed. | |
| float result = ctEval!(abs(-3)); | |
| assert(result == 3); | |
| // Can be statically asserted. | |
| static assert(ctEval!(abs(-4)) == 4); | |
| static assert(ctEval!(abs( 9)) == 9); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import core.stdc.math : round; | |
| import std.conv : to; | |
| import std.math : abs, PI, sin; | |
| // `round` from the C standard library cannot be interpreted at compile | |
| // time, because it has no available source code. However the function | |
| // calls preceding `round` can be evaluated during compile time. | |
| int result = ctEval!(abs(sin(1.0)) * 180 / PI) | |
| .round() | |
| .to!int(); | |
| assert(result == 48); | |
| } |