// 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. Load more
// 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 index of the first matching value in `values`, or `0` if `value` is not _among `values`. The predicate `pred` is used to compare 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; } /// Ditto template 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 more efficient 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 each choice 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. $(D SwitchError) will also be thrown if one choice can return a value but a void choice 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, )());