Closure's (apply str) questions? - clojure

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))

Related

Why does clojure.core/rest output a list when input is a vector?

Why does clojure.core/rest output a list when input is a vector?
This creates an unexpected effect:
(conj [1 2 3] 4)
; => [1 2 3 4]
(conj (rest [1 2 3]) 4)
; => (4 2 3)
I know that "it calls seq on its argument" from the docs which creates this effect. I don't understand why this is the desired effect. As a naïve user, I would expect (rest [1 2 3]) to behave like (subvec [1 2 3] 1). I know I could just use subvec for my use case. For the sake of learning, I would like to understand the rationale of rest, and use cases where outputting a list is desirable (even when the input is a vector).
The output of rest is NOT a list, but a seq, which is an even lower level abstraction. From the official documentation for rest:
Returns a possibly empty seq of the items after the first. Calls seq on its
argument.
The confusion arises from the fact that both are printed between parens, but if you look closely, they are different:
user=> (list? (rest [1 2 3]))
false
user=> (seq? (rest [1 2 3]))
true
How it's a seq different from a list? seqs are implemented with an Interface that requires implementing first, rest and cons, but details are up to the collection implementation. For instance, vectors use their own implementation:
user=> (class (rest [1 2 3]))
clojure.lang.PersistentVector$ChunkedSeq
user=> (class (rest '(1 2 3)))
clojure.lang.PersistentList
List are an implementation that at least extends a basic Seq interface, and builds on top. For instance, clojure.lang.PersistentList implements the Counted interface which requires a constant-time version of count.
For a detailed description of the differences between Seqs and Lists, check these links:
Differences between a seq and a list
https://clojure.org/reference/sequences
You make a good case for rest on a vector returning a vector. The trouble is that rest is one of the fundamental operations on sequences, and a vector is not a sequence:
=> (seq? [1 2 3 4])
false
However, if rest can accept a seqable thing such as a vector, you could say that it ought to be able to return such.
What does it return?
=> (type (rest [1 2 3 4]))
clojure.lang.PersistentVector$ChunkedSeq
This gives every appearance of being a subvec wrapped in a seq call.
I know that "it calls seq on its argument"
That is correct. Seqs are implemented with an Interface (ISeq) that requires implementing first, rest and cons.
rest takes any Seq'able (any collection that implements ISequable). The reason for using this is efficiency and simplicity.
The way different collection works, the most efficient way of getting the first and rest is different.
Which is why when you convert one collection into a seq, it will come with the most efficient implementation on rest and the others.
I hope this was clear
I agree that this behavior is unexpected and counterintuitive. As a workaround, I created the append and prepend functions in the Tupelo library.
From the docs, we see examples:
Clojure has the cons, conj, and concat functions, but it is not obvious how they should be used to add a new value to the beginning of a vector or list:
; Add to the end
> (concat [1 2] 3) ;=> IllegalArgumentException
> (cons [1 2] 3) ;=> IllegalArgumentException
> (conj [1 2] 3) ;=> [1 2 3]
> (conj [1 2] 3 4) ;=> [1 2 3 4]
> (conj '(1 2) 3) ;=> (3 1 2) ; oops
> (conj '(1 2) 3 4) ;=> (4 3 1 2) ; oops
; Add to the beginning
> (conj 1 [2 3] ) ;=> ClassCastException
> (concat 1 [2 3] ) ;=> IllegalArgumentException
> (cons 1 [2 3] ) ;=> (1 2 3)
> (cons 1 2 [3 4] ) ;=> ArityException
> (cons 1 '(2 3) ) ;=> (1 2 3)
> (cons 1 2 '(3 4) ) ;=> ArityException
Do you know what conj does when you pass it nil instead of a sequence? It silently replaces it with an empty list: (conj nil 5) ⇒ (5) This can cause you to accumulate items in reverse order if you aren’t aware of the default behavior:
(-> nil
(conj 1)
(conj 2)
(conj 3))
;=> (3 2 1)
These failures are irritating and unproductive, and the error messages don’t make it obvious what went wrong. Instead, use the simple prepend and append functions to add new elements to the beginning or end of a sequence, respectively:
(append [1 2] 3 ) ;=> [1 2 3 ]
(append [1 2] 3 4) ;=> [1 2 3 4]
(prepend 3 [2 1]) ;=> [ 3 2 1]
(prepend 4 3 [2 1]) ;=> [4 3 2 1]
Both prepend and append always return a vector result.

Single duplicate in a vector

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

Whats the difference between (concat [x] y) and (cons x y)?

I am stuck at the Pascal's Trapezoid from 4Clojure site, where you need to build a lazy sequence of the trapezoid's numbers.
My first shot was this:
(defn pascal [x]
(cons x
(lazy-seq
(pascal
(map +
(cons 0 x)
(conj x 0)
)))))
Which didn't work:
user=> (take 5 (pascal [1 1]))
([1 1] (1 2 1) (0 2 4 2) (0 0 4 8 4) (0 0 0 8 16 8))
Writing it this way works, however:
(defn pascal2 [x]
(cons x
(lazy-seq
(pascal2
(map +
(concat [0] x)
(concat x [0])
)))))
user=> (take 5 (pascal2 [1 1]))
([1 1] (1 2 1) (1 3 3 1) (1 4 6 4 1) (1 5 10 10 5 1))
So, what exactly am I doing wrong here? What is the difference between cons/conj and concat?
As others stated conj inserts the element(s) it receives in a different position depending on the concrete collection type, see this SO question for more detailed information about the difference between conj and cons.
In the first version of your pascal function you are providing a vector as the initial argument so the expression (conj x 0) will insert the 0 at the end of the vector for the computation of the second element in the series, but since map returns a lazy sequence, when the third element is computed the insertion happens at the beginning ((conj (map inc '(0)) 2) ;= (2 1)), which results in wrong elements in the series from then on.
In order to use the cons and conj approach you have to make sure you return a vector by using mapv instead of map.
(defn pascal [x]
(cons x
(lazy-seq
(pascal
(mapv +
(cons 0 x)
(conj x 0))))))
(take 5 (pascal [1 1]))
;= ([1 1] [1 2 1] [1 3 3 1] [1 4 6 4 1] [1 5 10 10 5 1])
The drawback with mapv is that it is eager so it will compute all members in the pascal element, instead of just holding it back until you actually need them.
On the other hand, when using concat you do ensure you append the element at the end and that everything is lazy, but the append is not done cheaply like with vectors, see here for more information.
Regardless of these factors you can still use cons in both cases, since what it does is what you need in either case (i.e. have an element inserted at the beginning of a collection).
(defn pascal2 [x]
(cons x
(lazy-seq
(pascal2
(map +
(cons 0 x) ; Replaced concat with cons
(concat x [0]))))))
(take 5 (pascal2 [1 1]))
;= ([1 1] (1 2 1) (1 3 3 1) (1 4 6 4 1) (1 5 10 10 5 1))
According to ClojureDocs
conj
conj clojure.core
(conj coll x)
(conj coll x & xs)
conj[oin]. Returns a new collection with the xs 'added'. (conj nil
item) returns (item). The 'addition' may happen at different 'places'
depending on the concrete type.
conj accepts the first argument as a collection, which means coll must be a collection type. conj will return a new collection with x added into coll, and the place of x added is depending on the type of coll.
e.g.
> (conj [1] [0])
[1 [0]] ; See [0] is added into [1] as an element. Instead of returning [1 0], it returns [1 [0]]
> (conj [1] 0)
[1 0]
> (conj '(1) 0)
(0 1) ;See the element `0` position is different in each case.
concat
concat clojure.core
(concat)
(concat x)
(concat x y)
(concat x y & zs)
Returns a lazy seq representing the concatenation of the elements in
the supplied colls.
concat accepts all the argument as collection types, which is different from conj. concat returns the concatenation of arguments.
e.g.
> (concat [0] [1])
(0 1)
> (concat [0] [[1]])
(0 [1])
> (concat [0] 1) ;See the second argument is not a collection type, thus the function throws an exception.
java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
>
cons
cons clojure.core
(cons x seq)
Returns a new seq where x is the first element and seq is the rest.
The doc of cons states clearly how cons would work. The second argument of cons must be a seq.
e.g.
> (cons [1] [0])
([1] 0) ; [1] is the first element and (0) is the rest
> (first (cons [1] [0]))
[1]
> (rest (cons [1] [0]))
(0)
> (cons 1 [0]) ; 1 is the first element and (0) is the rest
(1 0)
> (cons [1] 0) ;the second argument is not a seq, throwing exception
java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
conj on a list adds the element to the front of the list.
If you convert the list to a vector it will work.
user=> (conj '(1 2 3) 4)
(4 1 2 3)
user=> (conj [1 2 3] 4)
[1 2 3 4]

Clojure macros and quotes

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

Clojure: Difference between a list and a function that returns a list

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))