Skip to content

Instantly share code, notes, and snippets.

@edbond
Created November 6, 2010 13:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edbond/665401 to your computer and use it in GitHub Desktop.
Save edbond/665401 to your computer and use it in GitHub Desktop.
Xor macro in clojure, with tests
(ns xor
(:use clojure.test))
(defmacro xor
"Evaluates exprs one at a time, from left to right. If only one form returns
a logical true value (neither nil nor false), returns true. If more than one
value returns logical true or no value returns logical true, retuns a logical
false value. As soon as two logically true forms are encountered, no
remaining expression is evaluated. (xor) returns nil."
([] nil)
([f & r]
`(loop [t# false f# '[~f ~@r]]
(if-not (seq f#) t#
(let [fv# (eval (first f#))]
(cond
(and t# fv#) false
(and (not t#) fv#) (recur true (rest f#))
:else (recur t# (rest f#))))))))
(def test-output (ref []))
(defn- doit
"Returns val and creates the side effect of printing num."
[num val]
(dosync
(alter test-output conj num))
val)
(defn get-test-output
"Returns test-output content and clears it"
[]
(let [t @test-output]
(dosync (ref-set test-output []))
t))
(deftest nullary
(is (not (xor))))
(deftest unary
(is (get-test-output))
(is (xor (doit 1 true)))
(is (= [1] (get-test-output)))
(is (not (xor (doit 1 nil))))
(is (= [1] (get-test-output))))
(deftest binary
(is (not (xor (doit 1 nil) (doit 2 nil))))
(is (= [1 2] (get-test-output)))
(is (not (xor (doit 1 true) (doit 2 true))))
(is (= [1 2] (get-test-output)))
(is (xor (doit 1 nil) (doit 2 true)))
(is (= [1 2] (get-test-output)))
(is (xor (doit 1 true) (doit 2 nil)))
(is (= [1 2] (get-test-output))))
(deftest ternary
(is (not (xor (doit 1 nil) (doit 2 nil) (doit 3 nil))))
(is (= [1 2 3] (get-test-output)))
(is (xor (doit 1 nil) (doit 2 nil) (doit 3 true)))
(is (= [1 2 3] (get-test-output)))
(is (xor (doit 1 nil) (doit 2 true) (doit 3 nil)))
(is (= [1 2 3] (get-test-output)))
(is (not (xor (doit 1 nil) (doit 2 true) (doit 3 true))))
(is (= [1 2 3] (get-test-output)))
(is (xor (doit 1 true) (doit 2 nil) (doit 3 nil)))
(is (= [1 2 3] (get-test-output)))
(is (not (xor (doit 1 true) (doit 2 nil) (doit 3 true))))
(is (= [1 2 3] (get-test-output)))
(is (not (xor (doit 1 true) (doit 2 true) (doit 3 nil))))
(is (= [1 2] (get-test-output)))
(is (not (xor (doit 1 true) (doit 2 true) (doit 3 true))))
(is (= [1 2] (get-test-output))))
(deftest n-ary
(is (not (xor (doit 1 nil) (doit 2 nil) (doit 3 nil)
(doit 4 nil) (doit 5 nil) (doit 6 nil))))
(is (= [1 2 3 4 5 6] (get-test-output)))
(is (not (xor (doit 1 nil) (doit 2 true) (doit 3 nil)
(doit 4 true) (doit 5 nil) (doit 6 nil))))
(is (= [1 2 3 4] (get-test-output)))
(is (xor (doit 1 true) (doit 2 nil) (doit 3 nil)
(doit 4 nil) (doit 5 nil) (doit 6 nil)))
(is (= [1 2 3 4 5 6] (get-test-output)))
(is (not (xor (doit 1 true) (doit 2 true) (doit 3 true)
(doit 4 true) (doit 5 true) (doit 6 true))))
(is (= [1 2] (get-test-output)))
(is (not (xor (doit 1 nil) (doit 2 nil) (doit 3 nil)
(doit 4 nil) (doit 5 true) (doit 6 true))))
(is (= [1 2 3 4 5 6] (get-test-output))))
(run-tests)
@tsdh
Copy link

tsdh commented Jun 26, 2013

XOR has to return true if an odd number of its args are true, not if there's only one truthy argument.

@JBlaz
Copy link

JBlaz commented May 3, 2022

XOR has to return true if an odd number of its args are true, not if there's only one truthy argument.

That is one definition of how xor should work. Another (this) implementation is the result of the XOR is true if and only if 1 input is true. Neither is wrong it just depends on how it is implemented.

@KaliszAd
Copy link

XOR has to return true if an odd number of its args are true, not if there's only one truthy argument.

Wikipedia seems to side with you: https://en.wikipedia.org/wiki/Exclusive_or

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment