mirror of
https://github.com/dlang/phobos.git
synced 2025-04-26 21:22:20 +03:00
2226 lines
61 KiB
D
2226 lines
61 KiB
D
// 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 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 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 curry))
|
|
$(TD Converts a multi-argument function into a series of single-argument
|
|
functions. f(x, y) == curry(f)(x)(y)
|
|
))
|
|
$(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.
|
|
))
|
|
$(TR $(TD $(LREF bind))
|
|
$(TD Passes the fields of a struct as arguments to a function.
|
|
))
|
|
$(TR $(TD $(LREF ctEval))
|
|
$(TD Enforces the evaluation of an expression during compile-time.
|
|
))
|
|
))
|
|
|
|
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);
|
|
}
|