I'm trying to 'get' clojure macros and write a tweaked version of the are macro in terms of the existing are macro.
The tweak I want is to have signature [argv expr args] instead of [argv expr & args]
So I've tried
(defmacro are2 [argv expr args] `(clojure.test/are ~arg ~expr ~#args))
which kind of works, except it expects an unquoted list:
(are2 [input] (= 0 input) (1 2 3))
where I'd rather it expect a quoted list:
(are2 [input] (= 0 input) '(1 2 3))
But that results in:
Unable to resolve symbol: quote in this context.
If I try
(are2 [input] (= 0 input) (list 1 2 3))
instead then list itself gets processed as an test case.
What have I not understood / how can I get past the quote in my macro
'(1 2 3) is expanding into (quote (1 2 3)) which has an extra quote symbol and one too many levels of list which you can see with macroexpand-1:
user> (macroexpand-1 '(are2 [input] (= 0 input) '(1 2 3)))
(clojure.test/are [input] (= 0 input) quote (1 2 3))
you can drop the quote from the list by wrapping it int first and rest
user> (defmacro are2 [argv expr args]
`(clojure.test/are ~argv ~expr ~#(first (rest args))))
#'user/are2
user> (macroexpand-1 '(are2 [input] (= 0 input) '(1 2 3)))
(clojure.test/are [input] (= 0 input) 1 2 3)
which then runs as a test:
user> (are2 [input] (= 0 input) '(1 2 3)
FAIL in clojure.lang.PersistentList$EmptyList#1 (NO_SOURCE_FILE:1)
expected: (= 0 1)
actual: (not (= 0 1))
FAIL in clojure.lang.PersistentList$EmptyList#1 (NO_SOURCE_FILE:1)
expected: (= 0 2)
actual: (not (= 0 2))
FAIL in clojure.lang.PersistentList$EmptyList#1 (NO_SOURCE_FILE:1)
expected: (= 0 3)
actual: (not (= 0 3))
false
Related
i have a list like '(1 2 3 1 4 1 1 6 8 9 0 1) (not actually of numbers, just as an example)
I want to keep all "1" and the element next to the "1".
So the result i would want is (1 2 1 4 1 1 6 1).
Coming from an imperative point of view i would iterate over the list with a for loop, find the "1" at a certain index i and then also keep the element at index i+1.
What would a functional, Clojure idiomatic way of solving this problem be?
Using reduce you can move along the original list building a new list as you go. The reducing function f is passed the new list up until now and the next element from the old list. If the list up until now ends with a 1, or the next element is a 1, add the element to the new list. Otherwise keep the new list as is and move along.
user> (def xs [1 2 3 1 4 1 1 6 8 9 0 1])
#'user/xs
user> (defn f [x y] (if (or (= 1 y) (= 1 (peek x))) (conj x y) x))
#'user/f
user> (reduce f [] xs)
[1 2 1 4 1 1 6 1]
When you can't think of anything clever with sequence combinators, write the recursion by hand. It's not exactly elegant, but it's lazy:
(defn keep-pairs [pred coll]
(lazy-seq
(if (empty? coll)
[]
(let [x (first coll)
xs (next coll)]
(if (pred x)
(cons x (when xs
(let [y (first xs)]
(concat (when-not (pred y) [y])
(keep-pairs pred xs)))))
(when xs
(keep-pairs pred xs)))))))
user> (keep-pairs #{1} [1 2 3 1 4 1 1 6 8 9 0 1])
(1 2 1 4 1 1 6 1)
user> (take 10 (keep-pairs #{1} (cycle [1 2 3])))
(1 2 1 2 1 2 1 2 1 2)
I think I'd prefer reduce for something like this, but here's another 'functional' way of looking at it:
You have a sequence of values that should produce a potentially smaller sequence of values based on some predicate (i.e. filtering) and that predicate needs look-ahead/-behind behavior.
A less common use for map is mapping over multiple sequences at once e.g. (map f coll1 coll2 coll3). If you pass in an "offset" version of the same collection it can be used for the look-ahead/-behind logic.
(defn my-pairs [coll]
(mapcat
(fn [prev curr]
(when (or (= 1 prev) (= 1 curr))
[curr]))
(cons ::none coll) ;; these values are used for look-behind
coll))
This is (ab)using mapcat behavior to combine the mapping/filtering into one step, but it could also be phrased with map + filter.
here's one more solution with clojure's seq processors composition:
(defn process [pred data]
(->> data
(partition-by pred)
(partition-all 2 1)
(filter (comp pred ffirst))
(mapcat #(concat (first %) (take 1 (second %))))))
user> (process #{1} [1 2 1 1 3 4 1 5 1])
;;=> (1 2 1 1 3 1 5 1)
user> (process #{1} [0 1 2 1 1 1 3 4 1 5 1 6])
;;=> (1 2 1 1 1 3 1 5 1 6)
Another idea that does not work since it misses a last one:
(def v [1 2 3 1 4 1 1 6 8 9 0 1])
(mapcat (fn [a b] (when (= a 1) [a b])) v (rest v))
;; => (1 2 1 4 1 1 1 6 1)
So use two arity version of mapcat over the vector and the vector shifted one to the right.
You could check that last 1 explicitly and add, then you get a less elegant working version:
(concat
(mapcat (fn [a b] (when (= a 1) [a b])) v (rest v))
(when (= (peek v) 1) [1]))
;; => (1 2 1 4 1 1 1 6 1)
When you need to loop over data and retain state, I think a plain-old loop/recur is the most straightforward technique:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn keep-pairs
[data]
(loop [result []
prev nil
remaining data]
(if (empty? remaining)
result
(let [curr (first remaining)
keep-curr (or (= 1 curr)
(= 1 prev))
result-next (if keep-curr
(conj result curr)
result)
prev-next curr
remaining-next (rest remaining)]
(recur result-next prev-next remaining-next)))))
(dotest
(let [data [1 2 3 1 4 1 1 6 8 9 0 1]]
(is= [1 2 1 4 1 1 6 1]
(keep-pairs data))))
(defn windowed-pred [n pred]
(let [window (atom [])]
(fn [rf]
(fn ([] (rf))
([acc] (rf acc))
([acc v]
(let [keep? (or (pred v) (some pred #window))]
(swap! window #(vec (take-last n (conj %1 %2))) v)
(if keep?
(rf acc v)
acc)))))))
(let [c [1 2 3 1 4 1 1 6 8 9 0 1]
pred #(= % 1)]
(eduction (windowed-pred 1 pred) c))
(defn last-or-first? [obj pair] (or (= obj (last pair)) (= obj (first pair))))
; to test, whether previous element or element is object
(defn back-shift [l] (cons nil (butlast l))) ;; back-shifts a list
(defn keep-with-follower
[obj l]
(map #'last ; take only the element itself without its previous element
(filter #(last-or-first? obj %) ; is element or previous element the object?
(map #'list (back-shift l) l)))) ; group previous element and element in list
(def l '(1 2 3 1 4 1 1 6 8 9 0 1))
(keep-with-follower 1 l)
;; => (1 2 1 4 1 1 6 1)
A functional solution using only cons first last butlast list map filter = and defn and def.
Given a list of integers from 1 do 10 with size of 5, how do I check if there are only 2 same integers in the list?
For example
(check '(2 2 4 5 7))
yields yes, while
(check '(2 1 4 4 4))
or
(check '(1 2 3 4 5))
yields no
Here is a solution using frequencies to count occurrences and filter to count the number of values that occur only twice:
(defn only-one-pair? [coll]
(->> coll
frequencies ; map with counts of each value in coll
(filter #(= (second %) 2)) ; Keep values that have 2 occurrences
count ; number of unique values with only 2 occurrences
(= 1))) ; true if only one unique val in coll with 2 occurrences
Which gives:
user=> (only-one-pair? '(2 1 4 4 4))
false
user=> (only-one-pair? '(2 2 4 5 7))
true
user=> (only-one-pair? '(1 2 3 4 5))
false
Intermediate steps in the function to get a sense of how it works:
user=> (->> '(2 2 4 5 7) frequencies)
{2 2, 4 1, 5 1, 7 1}
user=> (->> '(2 2 4 5 7) frequencies (filter #(= (second %) 2)))
([2 2])
user=> (->> '(2 2 4 5 7) frequencies (filter #(= (second %) 2)) count)
1
Per a suggestion, the function could use a more descriptive name and it's also best practice to give predicate functions a ? at the end of it in Clojure. So maybe something like only-one-pair? is better than just check.
Christian Gonzalez's answer is elegant, and great if you are sure you are operating on a small input. However, it is eager: it forces the entire input list even when itcould in principle tell sooner that the result will be false. This is a problem if the list is very large, or if it is a lazy list whose elements are expensive to compute - try it on (list* 1 1 1 (range 1e9))! I therefore present below an alternative that short-circuits as soon as it finds a second duplicate:
(defn exactly-one-duplicate? [coll]
(loop [seen #{}
xs (seq coll)
seen-dupe false]
(if-not xs
seen-dupe
(let [x (first xs)]
(if (contains? seen x)
(and (not seen-dupe)
(recur seen (next xs) true))
(recur (conj seen x) (next xs) seen-dupe))))))
Naturally it is rather more cumbersome than the carefree approach, but I couldn't see a way to get this short-circuiting behavior without doing everything by hand. I would love to see an improvement that achieves the same result by combining higher-level functions.
(letfn [(check [xs] (->> xs distinct count (= (dec (count xs)))))]
(clojure.test/are [input output]
(= (check input) output)
[1 2 3 4 5] false
[1 2 1 4 5] true
[1 2 1 2 1] false))
but I like a shorter (but limited to exactly 5 item lists):
(check [xs] (->> xs distinct count (= 4)))
In answer to Alan Malloy's plea, here is a somewhat combinatory solution:
(defn check [coll]
(let [accums (reductions conj #{} coll)]
(->> (map contains? accums coll)
(filter identity)
(= (list true)))))
This
creates a lazy sequence of the accumulating set;
tests it against each corresponding new element;
filters for the true cases - those where the element is already present;
tests whether there is exactly one of them.
It is lazy, but does duplicate the business of scanning the given collection. I tried it on Alan Malloy's example:
=> (check (list* 1 1 1 (range 1e9)))
false
This returns instantly. Extending the range makes no difference:
=> (check (list* 1 1 1 (range 1e20)))
false
... also returns instantly.
Edited to accept Alan Malloy's suggested simplification, which I have had to modify to avoid what appears to be a bug in Clojure 1.10.0.
you can do something like this
(defn check [my-list]
(not (empty? (filter (fn[[k v]] (= v 2)) (frequencies my-list)))))
(check '(2 4 5 7))
(check '(2 2 4 5 7))
Similar to others using frequencies - just apply twice
(-> coll
frequencies
vals
frequencies
(get 2)
(= 1))
Positive case:
(def coll '(2 2 4 5 7))
frequencies=> {2 2, 4 1, 5 1, 7 1}
vals=> (2 1 1 1)
frequencies=> {2 1, 1 3}
(get (frequencies #) 2)=> 1
Negative case:
(def coll '(2 1 4 4 4))
frequencies=> {2 1, 1 1, 4 3}
vals=> (1 1 3)
frequencies=> {1 2, 3 1}
(get (frequencies #) 2)=> nil
I'm a Clojure newbie. I'm trying to understand why the second form doesn't work:
First form:
user=>(def nums(range 3))
(0 1 2)
user=>(map #(list %1) nums)
((0) (1) (2))
Second form:
user=> (map #(list %1) (0 1 2))
java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn
(NO_SOURCE_FILE:0)
The problem is the expression (0 1 2), which is interpreted as 0 applied to 1 and 2; that's impossible because 0 isn't a function.
(map #(list %1) '(0 1 2))
works as intended, though.
Because (0 1 2) means call function 0 with args 1 and 2, but 0 is not a function. So you need to make is a list rather than function application using quote or list function i.e '(0 1 2)
OR (list 0 1 2)
larsmans and Ankur have it. I realize this is a trivial example, but it would probably be more idiomatic to use a vector rather than a list:
(map #(list %1) [0 1 2])
You can also use % instead of %1 when there's only one arg passed to the anonymous function.
(map #(list %) [0 1 2])
user=> (map list (range 3))
((0) (1) (2))
user=> (map list '(0 1 2))
((0) (1) (2))
user=> (map list [0 1 2])
((0) (1) (2))
Why doesn't this produce the output I expect?
(defn test-fn []
(do
(println "start")
(map #(println (+ % 1)) '(1 2 3))
(println "done")))
It outputs
start
done
Whereas I would expect
start
2 3 4
done
map is lazy, and do does not force it. If you want to force the evaluation of a lazy sequence, use doall or dorun.
(defn test-fn []
(do
(println "start")
(dorun (map #(println (+ % 1)) '(1 2 3)))
(println "done")))
With closure
(apply str [\a \b])
and
(apply str '(\a \b))
returns "ab".
(apply str (\a \b))
returns an error.
Why is that?
Because (\a \b) means "call the function \a with an argument of \b", and since the character \a is not a function, it fails. Note the difference in the following:
user=> (+ 1 2 3)
6
user=> '(+ 1 2 3)
(+ 1 2 3)
As a general rule, if you want to write a literal sequence, use a vector instead of a quoted list since the quote will also stop evaluation of the parts inside the list, e.g.:
user=> [(+ 1 2) (+ 3 4)]
[3 7]
user=> '((+ 1 2) (+ 3 4))
((+ 1 2) (+ 3 4))