Wolfgang Thallergcc16 merge934c3885 months agoHistory
2538 lines · 78.1 KB · D
RawDownload
// Written in the D programming language./**This is a submodule of $(MREF std, algorithm).It contains generic comparison algorithms. $(SCRIPT inhibitQuickIndex = 1;)$(BOOKTABLE Cheat Sheet,$(TR $(TH Function Name) $(TH Description))$(T2 among,        Checks if a value is among a set of values, e.g.        `if (v.among(1, 2, 3)) // `v` is 1, 2 or 3`)$(T2 castSwitch,        `(new A()).castSwitch((A a)=>1,(B b)=>2)` returns `1`.)$(T2 clamp,        `clamp(1, 3, 6)` returns `3`. `clamp(4, 3, 6)` returns `4`.)$(T2 cmp,        `cmp("abc", "abcd")` is `-1`, `cmp("abc", "aba")` is `1`,        and `cmp("abc", "abc")` is `0`.)$(T2 either,        Return first parameter `p` that passes an `if (p)` test, e.g.        `either(0, 42, 43)` returns `42`.)$(T2 equal,        Compares ranges for element-by-element equality, e.g.        `equal([1, 2, 3], [1.0, 2.0, 3.0])` returns `true`.)$(T2 isPermutation,        `isPermutation([1, 2], [2, 1])` returns `true`.)$(T2 isSameLength,        `isSameLength([1, 2, 3], [4, 5, 6])` returns `true`.)$(T2 levenshteinDistance,        `levenshteinDistance("kitten", "sitting")` returns `3` by using        the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance,        Levenshtein distance algorithm).)$(T2 levenshteinDistanceAndPath,        `levenshteinDistanceAndPath("kitten", "sitting")` returns        `tuple(3, "snnnsni")` by using the        $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance,        Levenshtein distance algorithm).)$(T2 max,        `max(3, 4, 2)` returns `4`.)$(T2 min,        `min(3, 4, 2)` returns `2`.)$(T2 mismatch,        `mismatch("oh hi", "ohayo")` returns `tuple(" hi", "ayo")`.)$(T2 predSwitch,        `2.predSwitch(1, "one", 2, "two", 3, "three")` returns `"two"`.)) Copyright: Andrei Alexandrescu 2008-. License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/algorithm/comparison.d) Macros:T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) */module std.algorithm.comparison; import std.functional : unaryFun, binaryFun, lessThan, greaterThan;import std.range.primitives;import std.traits;import std.meta : allSatisfy, anySatisfy;import std.typecons : tuple, Tuple, Flag, Yes; import std.internal.attributes : betterC; /**Find `value` _among `values`, returning the 1-based indexof the first matching value in `values`, or `0` if `value`is not _among `values`. The predicate `pred` is used tocompare values, and uses equality by default. Params:    pred = The predicate used to compare the values.    value = The value to search for.    values = The values to compare the value to. Returns:    0 if value was not found among the values, otherwise the index of the    found value plus one is returned. See_Also:    $(UL    $(LI $(REF_SHORT find, std,algorithm,searching) and    $(REF_SHORT canFind, std,algorithm,searching) for finding a value in a range.)    $(LI $(REF_ALTTEXT `value in iota(start, end)`, iota, std,range) to find a value in    a particular interval.)    )*/uint among(alias pred = (a, b) => a == b, Value, Values...)    (Value value, Values values)if (Values.length != 0){    foreach (uint i, ref v; values)    {        import std.functional : binaryFun;        if (binaryFun!pred(value, v)) return i + 1;    }    return 0;} /// Dittotemplate among(values...)if (isExpressionTuple!values){    uint among(Value)(Value value)    if (!is(CommonType!(Value, values) == void))    {        switch (value)        {            foreach (uint i, v; values)                case v:                    return i + 1;            default:                return 0;        }    }} ///@safe @nogc @betterC unittest{    assert(3.among(1, 42, 24, 3, 2));     if (auto pos = "bar".among("foo", "bar", "baz"))        assert(pos == 2);    else        assert(false);     // 42 is larger than 24    assert(42.among!((lhs, rhs) => lhs > rhs)(43, 24, 100) == 2);} /**Alternatively, `values` can be passed at compile-time, allowing for a moreefficient search, but one that only supports matching on equality:*/@safe @nogc @betterC unittest{    assert(3.among!(2, 3, 4));    assert("bar".among!("foo", "bar", "baz") == 2);} @safe unittest{    import std.meta : AliasSeq;     if (auto pos = 3.among(1, 2, 3))        assert(pos == 3);    else        assert(false);    assert(!4.among(1, 2, 3));     auto position = "hello".among("hello", "world");    assert(position);    assert(position == 1);     alias values = AliasSeq!("foo", "bar", "baz");    auto arr = [values];    assert(arr[0 .. "foo".among(values)] == ["foo"]);    assert(arr[0 .. "bar".among(values)] == ["foo", "bar"]);    assert(arr[0 .. "baz".among(values)] == arr);    assert("foobar".among(values) == 0);     if (auto pos = 3.among!(1, 2, 3))        assert(pos == 3);    else        assert(false);    assert(!4.among!(1, 2, 3));     position = "hello".among!("hello", "world");    assert(position);    assert(position == 1);     static assert(!__traits(compiles, "a".among!("a", 42)));    static assert(!__traits(compiles, (Object.init).among!(42, "a")));} // Used in castSwitch to find the first choice that overshadows the last choice// in a tuple.private template indexOfFirstOvershadowingChoiceOnLast(choices...){    alias firstParameterTypes = Parameters!(choices[0]);    alias lastParameterTypes = Parameters!(choices[$ - 1]);     static if (lastParameterTypes.length == 0)    {        // If the last is null-typed choice, check if the first is null-typed.        enum isOvershadowing = firstParameterTypes.length == 0;    }    else static if (firstParameterTypes.length == 1)    {        // If the both first and last are not null-typed, check for overshadowing.        enum isOvershadowing =            is(firstParameterTypes[0] == Object) // Object overshadows all other classes!(this is needed for interfaces)            || is(lastParameterTypes[0] : firstParameterTypes[0]);    }    else    {        // If the first is null typed and the last is not - the is no overshadowing.        enum isOvershadowing = false;    }     static if (isOvershadowing)    {        enum indexOfFirstOvershadowingChoiceOnLast = 0;    }    else    {        enum indexOfFirstOvershadowingChoiceOnLast =            1 + indexOfFirstOvershadowingChoiceOnLast!(choices[1..$]);    }} /**Executes one of a series of handlers based on the dynamic type of a class instance. $(P`switchObject`'s dynamic type is checked for compatibility with the argument type of eachchoice in turn until a match is found. When there's a match, the choice will be called with`switchObject` cast to that type. If the choice returns a value, `castSwitch` will return it.)- If one choice's return type is void and one can return a value, `SwitchError`  will be thrown if the void handler was matched and completed execution without throwing.- A void handler is allowed to return when all the choices' return types convert to void -  in that case, `castSwitch` itself will return void.- A choice's return type can be `noreturn` (e.g. when a handler always throws an exception). Throws: If none of the choices match, a `SwitchError` will be thrown.  $(DSwitchError) will also be thrown if one choice can return a value but a voidchoice was matched and completed execution. Params:    choices = A sequence of function and/or delegate        handlers that each accept one argument. There can also be one choice that        accepts zero arguments, which will be invoked if the $(D        switchObject) is null.    switchObject = the object against which the tests are being made. Returns:    The return value of the selected choice.*/auto castSwitch(choices...)(Object switchObject){    import core.exception : SwitchError;    import std.format : format;     // Check to see if all handlers return void.    enum areAllHandlersVoidResult = {        bool result = true;        foreach (index, choice; choices)        {            result &= is(ReturnType!choice : void); // void or noreturn        }        return result;    }();     if (switchObject !is null)    {        // Checking for exact matches:        const classInfo = typeid(switchObject);        foreach (index, choice; choices)        {            static assert(isCallable!choice,                    "A choice handler must be callable");             alias choiceParameterTypes = Parameters!choice;            static assert(choiceParameterTypes.length <= 1,                    "A choice handler can not have more than one argument.");             static if (choiceParameterTypes.length == 1)            {                alias CastClass = choiceParameterTypes[0];                static assert(is(CastClass == class) || is(CastClass == interface),                        "A choice handler can only have a class or interface argument type.");                 // Check for overshadowing:                immutable indexOfOvershadowingChoice =                    indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]);                static assert(indexOfOvershadowingChoice == index,                        "choice number %d(type %s) is overshadowed by choice number %d(type %s)".format(                            index + 1, CastClass.stringof, indexOfOvershadowingChoice + 1,                            Parameters!(choices[indexOfOvershadowingChoice])[0].stringof));                 if (classInfo == typeid(CastClass))                {                    static if (is(ReturnType!(choice) == void))                    {                        choice(cast(CastClass) switchObject);                        static if (areAllHandlersVoidResult)                        {                            return;                        }                        else                        {                            throw new SwitchError("Handlers that return void should throw");                        }                    }                    else                    {                        return choice(cast(CastClass) switchObject);                    }                }            }        }         // Checking for derived matches:        foreach (choice; choices)        {            alias choiceParameterTypes = Parameters!choice;            static if (choiceParameterTypes.length == 1)            {                if (auto castedObject = cast(choiceParameterTypes[0]) switchObject)                {                    static if (is(ReturnType!(choice) == void))                    {                        choice(castedObject);                        static if (areAllHandlersVoidResult)                        {                            return;                        }                        else                        {                            throw new SwitchError("Handlers that return void should throw");                        }                    }                    else                    {                        return choice(castedObject);                    }                }            }        }    }    else // If switchObject is null:    {        // Checking for null matches:        foreach (index, choice; choices)        {            static if (Parameters!(choice).length == 0)            {                immutable indexOfOvershadowingChoice =                    indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]);                 // Check for overshadowing:                static assert(indexOfOvershadowingChoice == index,                        "choice number %d(null reference) is overshadowed by choice number %d(null reference)".format(                            index + 1, indexOfOvershadowingChoice + 1));                 if (switchObject is null)                {                    static if (is(ReturnType!(choice) == void))                    {                        choice();                        static if (areAllHandlersVoidResult)                        {                            return;                        }                        else                        {                            throw new SwitchError("Handlers that return void should throw");                        }                    }                    else                    {                        return choice();                    }                }            }        }    }     // In case nothing matched:    throw new SwitchError("Input not matched by any choice");} ///@system unittest{    import std.algorithm.iteration : map;    import std.format : format;     class A    {        int a;        this(int a) { this.a = a; }    }    interface I { }    class B : I { }     Object[] arr = [new A(1), new B(), null];     auto results = arr.map!(castSwitch!(                                (A a) => "A with a value of %d".format(a.a),                                (I i) => "derived from I",                                ()    => "null reference",                            ))();     // A is handled directly:    assert(results[0] == "A with a value of 1");    // B has no handler - it is handled by the handler of I:    assert(results[1] == "derived from I");    // null is handled by the null handler:    assert(results[2] == "null reference");} /// Using with noreturn/void handlers:@system unittest{    import core.exception : SwitchError;    import std.exception : assertThrown;     class A { }    class B { }     // B's handler never returns, so `i` does not need a value    int i;    assertThrown!Exception(        i = new B().castSwitch!(            (A a) => 1,            (B b) { throw new Exception("B is not allowed!"); }        )()    );     // Void handler call will throw if another handler returns a value    assertThrown!SwitchError(        i = new B().castSwitch!(            (A a) => 1,            (B b) {}        )()    );     // Void handlers are allowed if all the handlers convert to void:    new A().castSwitch!(        (A a) { },        (B b) { assert(false); },    )();} @system unittest{    import core.exception : SwitchError;    import std.exception : assertThrown;     interface I { }    class A : I { }    class B { }     // Nothing matches:    assertThrown!SwitchError((new A()).castSwitch!(                                 (B b) => 1,                                 () => 2,                             )());     // Choices with multiple arguments are not allowed:    static assert(!__traits(compiles,                            (new A()).castSwitch!(                                (A a, B b) => 0,                            )()));     // Only callable handlers allowed:    static assert(!__traits(compiles,                            (new A()).castSwitch!(                                1234,                            )()));     // Only object arguments allowed:    static assert(!__traits(compiles,                            (new A()).castSwitch!(                                (int x) => 0,                            )()));     // Object overshadows regular classes:    static assert(!__traits(compiles,                            (new A()).castSwitch!(                                (Object o) => 0,                                (A a) => 1,                            )()));     // Object overshadows interfaces:    static assert(!__traits(compiles,                            (new A()).castSwitch!(                                (Object o) => 0,                                (I i) => 1,                            )()));     // No multiple null handlers allowed:    static assert(!__traits(compiles,                            (new A()).castSwitch!(                                () => 0,                                () => 1,                            )()));     // No non-throwing void handlers allowed(when there are non-void handlers):    assertThrown!SwitchError((new A()).castSwitch!(                                 (A a)    {},                                 (B b) => 2,                             )());

Lines 1–500 of 2538.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

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

Repository code

Go to file
t