From ca8b6b4fe5ba3dfb85058cb502b907a944685f55 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Sun, 2 Aug 2015 23:57:46 +0300 Subject: [PATCH 1/2] Add exclusive or logical operator Added xor to complement and, or, not operators. Standard python falsey/truthy semantics are followed. This implementation works for two or more parameters. --- docs/language/api.rst | 22 ++++++++++++++++++++++ hy/core/macros.hy | 5 +++++ tests/native_tests/language.hy | 17 +++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/docs/language/api.rst b/docs/language/api.rst index 67ef630..ab58065 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -1511,6 +1511,28 @@ expands to: Section :ref:`using-gensym` +xor +--- + +.. versionadded:: 0.12.0 + +``xor`` is used in logical expressions to perform exclusive or. It takes at +least two parameters. It returns ``True`` if exactly one of the parameters +evaluates to ``True``. In all other cases ``False`` is returned. Example +usage: + +.. code-block:: clj + + => (xor True False) + True + + => (xor True True) + False + + => (xor [] [] [0]) + True + + yield ----- diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 014920c..dce89c6 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -237,3 +237,8 @@ (let [[decorators (slice expr nil -1)] [fndef (get expr -1)]] `(with-decorator ~@decorators ~fndef))) + +(defmacro xor [&rest args] + "perform exclusive or comparison between all arguments" + (when (< (len args) 2) (macro-error nil "xor requires at least two arguments.")) + `(= (reduce (fn [a b] (if b (inc a) a)) ~args 0) 1)) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 352ecf5..5ced0cf 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -813,6 +813,23 @@ (assert (= a 1))) +(defn test-xor [] + "NATIVE: test the xor macro" + (let [[xor-both-true (xor true true)] + [xor-both-false (xor false false)] + [xor-true-false (xor true false)] + [xor-one-true (xor false true false)] + [xor-one-false (xor true false true)] + [xor-all-true (xor true true true)] + [xor-all-false (xor false false false)]] + (assert (= xor-both-true false)) + (assert (= xor-both-false false)) + (assert (= xor-true-false true)) + (assert (= xor-one-true true)) + (assert (= xor-one-false false)) + (assert (= xor-all-true false)) + (assert (= xor-all-false false)))) + (defn test-if-return-branching [] "NATIVE: test the if return branching" ; thanks, algernon From eaf1a3023aadea124d06d695ab8016595f278368 Mon Sep 17 00:00:00 2001 From: Tuukka Turto Date: Mon, 3 Aug 2015 05:37:39 +0300 Subject: [PATCH 2/2] Change xor to binary function xor with more than two input parameters is not well defined and people have different expectations on how it should behave. Avoid confusion by sticking with two parameters only. --- docs/language/api.rst | 9 ++++----- hy/core/language.hy | 8 +++++++- hy/core/macros.hy | 5 ----- tests/native_tests/language.hy | 12 ++---------- 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index ab58065..5ddbe39 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -1516,10 +1516,9 @@ xor .. versionadded:: 0.12.0 -``xor`` is used in logical expressions to perform exclusive or. It takes at -least two parameters. It returns ``True`` if exactly one of the parameters -evaluates to ``True``. In all other cases ``False`` is returned. Example -usage: +``xor`` is used in logical expressions to perform exclusive or. It takes two +parameters. It returns ``True`` if only of the parameters is ``True``. In all +other cases ``False`` is returned. Example usage: .. code-block:: clj @@ -1529,7 +1528,7 @@ usage: => (xor True True) False - => (xor [] [] [0]) + => (xor [] [0]) True diff --git a/hy/core/language.hy b/hy/core/language.hy index d70e84b..ad5ee35 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -425,6 +425,11 @@ (hyify (. value __name__)) (catch [] (string value)))))) +(defn xor [a b] + "Perform exclusive or between two parameters" + (or (and a (not b)) + (and b (not a)))) + (def *exports* '[Botsbuildbots butlast calling-module-name coll? cons cons? cycle dec distinct disassemble drop drop-last drop-while empty? even? @@ -434,4 +439,5 @@ keyword? last list* macroexpand macroexpand-1 map merge-with name neg? nil? none? nth numeric? odd? pos? range read read-str remove repeat repeatedly rest reduce second some string string? - symbol? take take-nth take-while zero? zip zip_longest zipwith]) + symbol? take take-nth take-while xor zero? zip zip_longest + zipwith]) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index dce89c6..014920c 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -237,8 +237,3 @@ (let [[decorators (slice expr nil -1)] [fndef (get expr -1)]] `(with-decorator ~@decorators ~fndef))) - -(defmacro xor [&rest args] - "perform exclusive or comparison between all arguments" - (when (< (len args) 2) (macro-error nil "xor requires at least two arguments.")) - `(= (reduce (fn [a b] (if b (inc a) a)) ~args 0) 1)) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 5ced0cf..00b7a99 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -817,18 +817,10 @@ "NATIVE: test the xor macro" (let [[xor-both-true (xor true true)] [xor-both-false (xor false false)] - [xor-true-false (xor true false)] - [xor-one-true (xor false true false)] - [xor-one-false (xor true false true)] - [xor-all-true (xor true true true)] - [xor-all-false (xor false false false)]] + [xor-true-false (xor true false)]] (assert (= xor-both-true false)) (assert (= xor-both-false false)) - (assert (= xor-true-false true)) - (assert (= xor-one-true true)) - (assert (= xor-one-false false)) - (assert (= xor-all-true false)) - (assert (= xor-all-false false)))) + (assert (= xor-true-false true)))) (defn test-if-return-branching [] "NATIVE: test the if return branching"