Clojure, can not find an error in the code - clojure

(defn mapset [func ele]
(loop [elements ele
result []]
(if (empty? elements)
(set result)
(let [[first-value & another] elements]
(into result (func first-value))
(recur another result)))))
(def v [1 2 3 4 5])
(mapset + v)
Exeption:
Don't know how to create ISeq from: java.lang.Long
Who knows how to fix it?

The first issue is that into accepts collections, not a collection and a single element. I think you wanted to use conj instead:
(conj result (func first-value))
Another issue is that Clojure collections (result vector in this case) are immutable thus functions like conj or into return a new updated collection instead of modifying their input parameters thus you need to use their result for recur:
(recur another (conj result (func first-value)))
And the last issue is that you are passing + function which when applied to a single argument will return it. I guess you wanted to use inc instead.
Thus your working code should look like:
(defn mapset [func ele]
(loop [elements ele
result []]
(if (empty? elements)
(set result)
(let [[first-value & another] elements]
(recur another (conj result (func first-value)))))))
(def v [1 2 3 4 5])
(mapset inc v)
;; => #{4 6 3 2 5}

Related

Clojure function to Replace Count

I need help with an assignment that uses Clojure. It is very small but the language is a bit confusing to understand. I need to create a function that behaves like count without actually using the count funtion. I know a loop can be involved with it somehow but I am at a lost because nothing I have tried even gets my code to work. I expect it to output the number of elements in list. For example:
(defn functionname []
...
...)
(println(functionname '(1 4 8)))
Output:3
Here is what I have so far:
(defn functionname [n]
(def n 0)
(def x 0)
(while (< x n)
do
()
)
)
(println(functionname '(1 4 8)))
It's not much but I think it goes something like this.
This implementation takes the first element of the list and runs a sum until it can't anymore and then returns the sum.
(defn recount [list-to-count]
(loop [xs list-to-count sum 0]
(if (first xs)
(recur (rest xs) (inc sum))
sum
)))
user=> (recount '(3 4 5 9))
4
A couple more example implementations:
(defn not-count [coll]
(reduce + (map (constantly 1) coll)))
or:
(defn not-count [coll]
(reduce (fn [a _] (inc a)) 0 coll))
or:
(defn not-count [coll]
(apply + (map (fn [_] 1) coll)))
result:
(not-count '(5 7 8 1))
=> 4
I personally like the first one with reduce and constantly.

Sum equal adjacent integers

Test case:
(def coll [1 2 2 3 4 4 4 5])
(def p-coll (partition 2 1 coll))
;; ((1 2) (2 2) (2 3) (3 4) (4 4) (4 4) (4 5))
Expected output:
(2 2 4 4 4) => 16
Here is what I am to implement: Start with vector v [0]. Take each pair, if the first element of the pair is equal to the last element of the vector, or if the elements of the pair are equal, add the first item of the pair to v. (And finally reduce v to its sum.) The code below can do if the elements of the pair are equal part, but not the first part. (Thus I get (0 2 4 4). I guess the reason is that the elements are added to v at the very end. My questions:
What is the way to compare an element with the last selected element?
What other idiomatic ways are there to implement what I am trying achieve?
How else can I approach the problem?
(let [s [0]]
(concat s (map #(first %)
(filter #(or (= (first %) (first s)) (= (first %) (second %))) p-coll))))
You are on the right track with partitioning the data here. But there
is a nicer way to do that. You can use (partition-by identity coll)
to group consecutive, same elements.
Then just keep the ones with more than one elements and sum them all up.
E.g.
(reduce
(fn [acc xs]
(+ acc (apply + xs)))
0
(filter
next
(partition-by identity coll)))
Starting out from your initial partition, with p-coll being like you described above (i.e. a list of pairs), and v being the vector [0], you can do the following:
(reduce
(fn [vect [a b]]
(if (or (= a b) (= a (last vect)))
(conj vect a)
vect))
v p-coll)
;; => [0 2 2 4 4 4]
We start from the vector [0], and reduce p-coll by processing its elements one by one. If an element satisfies one of the two conditions you specified, then we conj it onto the initial vector. Otherwise, we leave the vector as is.
Finally, you can use apply + to sum the resulting vector and get your final answer.
Generally, when you need to process a collection (here, p-coll) and some partial answer (here, the vector v) into some sort of final answer (here, the vector [0 2 2 4 4 4]), reduce is the most idiomatic and purely functional approach. After having identified those components, it's just a matter of coming up with the appropriate function to put them together.
Another approach (less idiomatic, but easier to understand from a procedural standpoint) would be to use an atom for the vector v, and keep growing it as you process the list with doseq:
(def v (atom [0]))
(doseq [[a b] p-coll]
(if (or (= a b) (= a (last #v)))
(swap! v conj a)))
(println #v)
;; => [0 2 2 4 4 4]
A solution only with flatten and map:
(defn consecutives [lst]
(flatten (map (fn [[x y z]] (cond (= x y z) [z]
(= y z) [y z]
:else []))
(map #'vector lst (rest lst) (rest (rest lst))))))
Purely tail-recursive solution
which "keeps in memory" previous and previous-previous element.
(defn consecutives
[lst]
(loop [lst lst
acc []
last-val nil
last-last-val nil]
(cond (empty? lst) acc
:else (recur (rest lst)
(if (= (first lst) last-val)
(conj (if (= last-val last-last-val)
acc
(conj acc last-val))
(first lst))
acc)
(first lst)
last-val))))
(consecutives coll)
;; => [2 2 4 4 4]

Map a function on every two elements of a list

I need a function that maps a function only on every other element, e.g.
(f inc '(1 2 3 4))
=> '(2 2 4 4)
I came up with:
(defn flipflop [f l]
(loop [k l, b true, r '()]
(if (empty? k)
(reverse r)
(recur (rest k)
(not b)
(conj r (if b
(f (first k))
(first k)))))))
Is there a prettier way to achieve this ?
(map #(% %2)
(cycle [f identity])
coll)
It's a good idea to look at Clojure's higher level functions before using loop and recur.
user=> (defn flipflop
[f coll]
(mapcat #(apply (fn ([a b] [(f a) b])
([a] [(f a)]))
%)
(partition-all 2 coll)))
#'user/flipflop
user=> (flipflop inc [1 2 3 4])
(2 2 4 4)
user=> (flipflop inc [1 2 3 4 5])
(2 2 4 4 6)
user=> (take 11 (flipflop inc (range))) ; demonstrating laziness
(1 1 3 3 5 5 7 7 9 9 11)
this flipflop doesn't need to reverse the output, it is lazy, and I find it much easier to read.
The function uses partition-all to split the list into pairs of two items, and mapcat to join a series of two element sequences from the calls back into a single sequence.
The function uses apply, plus multiple arities, in order to handle the case where the final element of the partitioned collection is a singleton (the input was odd in length).
also, since you want to apply the function to some specific indiced items in the collection (even indices in this case) you could use map-indexed, like this:
(defn flipflop [f coll]
(map-indexed #(if (even? %1) (f %2) %2) coll))
Whereas amalloy's solution is the one, you could simplify your loop - recur solution a bit:
(defn flipflop [f l]
(loop [k l, b true, r []]
(if (empty? k)
r
(recur (rest k)
(not b)
(conj r ((if b f identity) (first k)))))))
This uses couple of common tricks:
If an accumulated list comes out in the wrong order, use a vector
instead.
Where possible, factor out common elements in a conditional.

seq to vec conversion - Key must be integer

I want to get the indices of nil elements in a vector eg.
[1 nil 3 nil nil 4 3 nil] => [1 3 4 7]
(defn nil-indices [vec]
(vec (remove nil? (map
#(if (= (second %) nil) (first %))
(partition-all 2 (interleave (range (count vec)) vec)))))
)
Running this code results in
java.lang.IllegalArgumentException: Key must be integer
(NO_SOURCE_FILE:0)
If I leave out the (vec) call surrounding everything, it seems to work, but returns a sequence instead of a vector.
Thank you!
Try this instead:
(defn nil-indices [v]
(vec (remove nil? (map
#(if (= (second %) nil) (first %))
(partition-all 2 (interleave (range (count v)) v))))))
Clojure is a LISP-1: It has a single namespace for both functions and data, so when you called (vec ...), you were trying to pass your result sequence to your data as a parameter, not to the standard-library vec function.
See other answer for your problem (you are shadowing vec), but consider using a simpler approach.
map can take multiple arguments, in which case they are passed as additional arguments to the map function, e.g. (map f c1 c2 ...) calls (f (first c1) (first c2) ...) etc, until one of the sequence arguments is exhausted.
This means your (partition-all 2 (interleave ...)) is a very verbose way of saying (map list (range) v). There is also a function map-indexed which does the same thing. However, it only takes one sequence argument, so (map-indexed f c1 c2) is not legal.
Here is your function rewritten for clarity using map-indexed, threading, and nil?:
(defn nil-indices [v]
; Note: map fn called like (f range-item v-item)
; Not like (f (range-item v-item)) as in your code.
(->> (map-indexed #(when (nil? %2) %1) v) ;; like (map #(when ...) (range) v)
(remove nil?)
vec))
However, you can do this instead with reduction and the reduce-kv function. This function is like reduce, except the reduction function receives three arguments instead of two: the accumulator, the key of the item in the collection (index for vectors, key for maps), and the item itself. Using reduce-kv you can rewrite this function even more clearly (and it will probably run faster, especially with transients):
(defn nil-indices [v]
(reduce-kv #(if (nil? %3) (conj %1 %2) %1) [] v))

how to use Conj function in Clojure

I am trying to achieve one of the functions in my program. I have list=[a b c] as a parameter in func3. I want to test an equality of these items. If they are not equal I will call it again using another list return from func2.
Here is what I need to do. I want the Conj to do this [list list1 list2 list3] until func3 have the items that are equal. In my function, I want conj to merge an empty vector r[] to other lists when the condition is false.All I get right now is a final list return when the condition is true. Assume that the condition must be false(items are not equal) before getting true. Can someone help me to use the conj in the false condition.Thank you.
;input [1 2 3]
;output [[1 2 3][1 3 4] [3 4 5] ]// numbers for demo only
(defn func3 [list]
(loop [v (vec list) r []]
(if(= (v 0) (v 1) (v 2))
(conj r v)
(let[result (func2 v)]
;i want to merge result to r until the condition is true
(conj r result)
(func3 result)))))
Conj never changes its input, it creates a new output based on the input. In the second condition, you are not doing anything to the output of conj, so its result is never used.
(defn func3 [list]
(loop [[v & vs] (vec list) r []]
(if (= (v 0) (v 1) (v 2))
(conj r v)
(let [result (func2 v)
r (conj r result)]
(recur vs r)))))
I understand that you have a list of three elements as an initial input, want to compare them for equality. In case they match, you want to append the list to a later returned accumulator - in case they don't, you want to transform the list using a function idiomatically called func2 and try that procedure again.
EDIT: Since you have commented how your function should work, here comes an implementation:
(defn func3
"Determines whether items in coll are equal. If not, transforms coll with func2 and
repeats the test until a coll with equal elements could be found.
Returns a vector of all tried versions."
[coll]
(loop [acc []
coll coll]
(if (apply = coll)
(conj acc coll)
(recur (conj acc coll)
(func2 coll)))))
Here is a lazy implementation:
(defn func3
[coll]
(-> (->> coll
(iterate func2)
(split-with (partial apply =)))
(as-> [dropped [r]]
(concat dropped [r])))