i'm trying to write a simple Clojure program to find the maximum element of a list. This is the code I wrote so far:
(ns iotest.core
(:gen-class))
(defn getLargest [L]
"Get the largest element of a given list L"
(cond
(empty? L) nil
(empty? (rest L)) (first L)
((> (first L) (getLargest (rest L)))) (first L)
:else (getLargest (rest L))))
(defn -main []
(let
[L (4 5 10 1)
largest (getLargest L)]
(println "Largest element of" L "is" largest)))
The problem is, I get the following error(i'm using leiningen):
class java.lang.Long cannot be cast to class clojure.lang.IFn (java.lang.Long is in module java.base of loader 'bootstrap';
clojure.lang.IFn is in unnamed module of loader 'app')
What I'm doing wrong?
I know about the max function, but I want to implement it by myself for learning purposes.
This part:
(let [L (4 5 10 1) ... )
is missing quote- or you can use vector instead:
(4 5 10 1)
error
'(4 5 10 1)
=> (4 5 10 1)
[4 5 10 1]
=> [4 5 10 1]
There is also error with ((> (first L) (getLargest (rest L)))) (first L) (class java.lang.Boolean cannot be cast to class clojure.lang.IFn, you have additional pair of parentheses here) and style error with getLargest- Clojure uses kebab-case, so correct name is get-largest.
For correct result check reduce. Min and max don't work for empty lists, so you have to be careful about that:
(defn get-largest [lst]
(if (empty? lst)
:none
(reduce (fn [acc element]
(if (> element acc) element acc))
(first lst)
(rest lst))))
(let [lst [4 5 10 1]
largest (get-largest lst)]
(println "Largest element of" lst "is" largest))
Largest element of [4 5 10 1] is 10
=> nil
(let [lst []
largest (get-largest lst)]
(println "Largest element of" lst "is" largest))
Largest element of [] is :none
=> nil
EDIT: This is a little bit better:
(defn get-largest [lst]
(when (seq lst)
(reduce #(if (> %2 %1) %2 %1)
(first lst)
(rest lst))))
(let [lst [4 5 10 1]]
(if-let [largest (get-largest lst)]
(println "Largest element of" lst "is" largest)
(println "Sequence is empty")))
Largest element of [4 5 10 1] is 10
=> nil
You have a number (java.lang.long) in a function-call position. Functions implement the interface IFn but long does not. That's what the error message is trying to tell you.
Did you spot it now?
It's the (4 5 10 1) part. Here, you're trying to call the function 4.
Best to replace it with a vector like so: [4 5 10 1].
I did not review other parts of the code. Take a look at reduce. It's pretty easy to implement max using reduce.
Related
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.
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]
I am trying to implement the Havel-Hakimi algorithm using Clojure.
The steps in the algorithm are:
1. Remove zeros and reverse sort a list of values from highest to lowest.
2. Check if the list of values is empty, in which case exit and return a value of true.
3. If not empty, check to see if the first value in the list is greater than the length of the rest of the list. If it is, return false, else remove the first entry in the list and repeat the process.
The following is my implementation in code.
(defn hh [lst]
(if-let [lst (empty? (remove #{0} (reverse (sort lst))))]
true
(if (> (first lst) (count (rest lst)))
false
(hh (map dec (rest lst))))))
The following are my test cases.
(deftest hh-test
(is (= true (hh [])))
(is (= false (hh [1])))
(is (= true (hh [1 1])))
(is (= false (hh [2 2])))
(is (= false (hh [2 2 0]))) ;;failing and i don't know why
(is (= false (hh [3 2 1])))
(is (= true (hh [3 1 2 3 1])))
(is (= false (hh [5 3 0 2 6 2 0 7 2 5])))) ;;also failing and I don't know why
The fifth test case is particularly troubling me because it should be equivalent to the previous test case (the zero should get immediately dropped from the list and would then be equivalent to the previous test case, but for some reason it isn't).
I'm still new and learning Clojure (and programming in general) and I haven't been able to get debugging in Cider working (I get some strange errors when trying to debug) so I'm a bit stuck. Any help is appreciated.
One thing you can do is require logging (or just use println)
(ns f
(:require [clojure.tools.logging :as log]))
And then proceed to log the crap (the technical term) out of your function:
(defn hhd [lst iter]
(log/info "iter:" iter ", lst is" lst)
(if-let [lst (empty? (remove #{0} (reverse (sort lst))))]
(do (log/info "returning true; lst = " lst) true)
(if (> (first lst) (count (rest lst)))
(do (log/info "returning false; lst =" lst) false)
(do (log/info "doing recursion; lst =" lst)
(hhd (map dec (rest lst)) (inc iter))))))
Running this on the problematic [2 2 0] we see:
f> (hhd [2 2 0] 0)
[nREPL-worker-19] 2019-08-06 04:25:41,213 INFO f: iter: 0 , lst is [2 2 0]
[nREPL-worker-19] 2019-08-06 04:25:41,214 INFO f: doing recursion; lst = [2 2 0]
[nREPL-worker-19] 2019-08-06 04:25:41,215 INFO f: iter: 1 , lst is (1 -1)
[nREPL-worker-19] 2019-08-06 04:25:41,215 INFO f: doing recursion; lst = (1 -1)
[nREPL-worker-19] 2019-08-06 04:25:41,215 INFO f: iter: 2 , lst is (-2)
[nREPL-worker-19] 2019-08-06 04:25:41,215 INFO f: doing recursion; lst = (-2)
[nREPL-worker-19] 2019-08-06 04:25:41,216 INFO f: iter: 3 , lst is ()
[nREPL-worker-19] 2019-08-06 04:25:41,216 INFO f: returning true; lst = true
true
Right off the bat we see that the lst value before the recursive step is the orignal list --- it isn't reverse sorted and it still has zeros!
Since (almost) everything in clojure is immutable, just calling sort and remove on the lst doesn't change the list. Wait? But aren't you setting the result of that to a local lst variable? Well, no, that local lst variable gets the result of calling empty?, which can only be a boolean. However, a quirky thing about if-let is that it only sets the variable if the test is true, otherwise it's like it never existed. And that's why the lst in your "else" clause is the original list (although without that quirk it would be set to "false", which isn't what you wanted, anyway).
The way to turn this around is first, to use seq instead of empty?. seq tests if a sequence is not empty, and if not takes the value of the sequence instead of a boolean, which is what you want here.
Then, second, since this is the opposite of your original test, you need to switch the "if" and "else" clauses:
(defn hh [lst]
(if-let [lst (seq (remove #{0} (reverse (sort lst))))]
(if (> (first lst) (count (rest lst)))
false
(hh (map dec (rest lst))))
true))
With that, your failing tests pass. But one of your passing tests fails. I think in that case the test is expecting the wrong value.
Jas has corrected the code you wrote into what you probably intended to write. But even corrected, it is not the Havel-Hakimi algorithm.
The problem is that the recursive call (hh (map dec (rest lst))), decrements all the numbers in (rest lst). It should only decrement (first lst) of them. Modifying Jas's code to do so, we get ...
(defn hh [lst]
(let [lst (remove #{0} (reverse (sort lst)))]
(println lst)
(if (empty? lst)
true
(let [n (first lst), ns (rest lst)]
(if (> n (count ns))
false
(let [front (take n ns), back (drop n ns)]
(hh (concat (map dec front) back))))))))
... where I've added a diagnostic println.
Everything now works as you expected, apart from the new test case:
=> (hh [5 3 0 2 6 2 0 7 2 5])
(7 6 5 5 3 2 2 2)
(5 4 4 2 1 1 1)
(3 3 1 1)
(2)
false
But this too looks right to me.
We can tidy the code up a bit:
(defn hh [lst]
(let [lst (filter pos? (reverse (sort lst)))]
(or (empty? lst)
(let [[n & ns] lst]
(and (<= n (count ns))
(let [[front back] (split-at n ns)]
(recur (concat (map dec front) back))))))))
(filter pos? ... ) replaces (remove #{0} ... ) as a predicate. This stops negative numbers looping indefinitely.
The or and and eliminate true and false.
recur replaces the recursive call of hh, possible since it's in tail position.
The destructuring is more concise.
A final point about the algorithm: it still works if you take out the sorting! This came as a surprise to me.
I am wondering how to remove duplicate elements from two sequences and combine two sequences. For instance,
user=>(remove-dup [1 4 7 10 16] [2 7 18 4])
(1 2 10 18 16)
My code is:
(defn remove-dup [l1 l2]
(let [list (concat l1 l2)]
(loop [l list res '()]
(if (>= (second (first (frequencies l))) 2)
(recur (rest l) res)
(recur (rest l) (conj res (first (first l))))))))
But when I run the code, I got the error message:
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:528)
How can I fix this code. Thanks!
Your error is here:
(first (first l))
Remember, l is the sequence of all the elements you haven't handled yet. For instance, in the first iteration of the loop, l might look like this:
(1 4 7 10 16 2 7 18 4)
You can see from this that (first l) would be 1, so (first (first l)) would be trying to treat a number as a sequence, which doesn't work.
If you replace (first (first l)) with just (first l), you'll get a NullPointerException because you don't have a base case: what should you do when l is empty? You might do something like this (where ,,, is a placeholder for your current if expression):
(if (empty? l)
res
,,,)
However, if we try to use the method now, we still don't get the right result:
(remove-dup [1 4 7 10 16] [2 7 18 4])
;=> (4 18 7 2 16 10 1)
Hrm.
I could try to fiddle with your code some more to get it to work, but there's a better way to solve this problem. Since you're trying to remove duplicates and you don't care about order, the functions in clojure.set are the right tool for the job here. I would write remove-dup like this:
(require '[clojure.set :as set])
(defn remove-dup [c1 c2]
(let [[s1 s2] (map set [c1 c2])]
(seq (set/difference (set/union s1 s2) (set/intersection s1 s2)))))
Example:
(remove-dup [1 4 7 10 16] [2 7 18 4])
;=> (1 2 16 10 18)
there is a number of fatal errors in your code:
The thing that breaks it, is (first (first l)), since l is the list of numbers, it throws an error when you try to take first item of number.
But there are more important ones:
first of all, even if your code were correct, it doesn't have any case to break the loop, so it would probably lead to the infinite loop (or exception of some kind). Second is your total misunderstanding of the frequencies usage. You can't rely on the order of the frequencies results, since it returns unordered map (not to mention it is beind called in every loop iteration, which is really bad for preformance).
That's how i would do something like this with a single pass over collections in loop:
(defn unique [coll1 coll2]
(let [items (concat coll1 coll2)]
(loop [res #{}
seen #{}
[x & xs :as items] items]
(cond ;; if there are no items left to check, returning result
(empty? items) res
;; if we've already seen the first item of a coll, remove it from the resulting set
(seen x) (recur (disj res x) seen xs)
;; otherwise mark it as seen, and add it to the result set
:else (recur (conj res x) (conj seen x) xs)))))
in repl:
user> (unique [1 4 7 10 16] [2 7 18 4])
#{1 2 16 10 18}
(defn remove-dupl [l1 l2]
(let [rmdup (fn [l1 l2] (remove (set l1) l2))]
(concat (rmdup l1 l2) (rmdup l2 l1))))
Try this solution
(defn remove-dup [l1 l2]
(let [ls (concat l1 l2)]
(loop [l (frequencies ls) res '()]
(if (empty? l) res
(if (>= (second (first l)) 2)
(recur (rest l) res)
(recur (rest l) (cons (first (first l)) res)))))))
The others have found your errors. I'd like to look at what you are trying to do.
Given that
the order is not important and
you are removing duplicate elements
this is the set operation exclusive or (XOR).
It is not included in clojure.set. We can either, as Sam Estep does, define it in terms of the operations we have, or write it more directly ourselves:
(defn exclusive-or [sa sb]
(if (<= (count sa) (count sb))
(reduce
(fn [ans a]
(if (contains? sb a)
(disj ans a)
(conj ans a)))
sb
sa)
(recur sb sa)))
We can then define
(defn remove-dup [xs ys]dited
(exclusive-or (set xs) (set ys))
For example,
(remove-dup [1 4 7 10 16] [2 7 18 4]) ;#{1 2 10 16 18}
Edited to correct error in exclusive-or.
I am trying to write a recursive sort function that sorts a list from low to high (duh). I am currently getting output, just not the correct output. Here is my code:
(defn sort/predicate [pred loi]
(if (empty? loi)
()
(if (= (count loi) 1)
(cons (first loi) (sort pred (rest loi)))
(if (pred (first loi) (first (rest loi)))
(cons (first loi) (sort pred (rest loi)))
(if (pred (first (rest loi)) (first loi))
(cons (first (rest loi)) (sort pred (cons (first loi) (rest (rest loi)))))
(cons (first loi) (sort pred (rest loi))))))))
Basically, I compare the first two elements in the list and, if the first element is smaller I cons it with the result of comparing the next two elements of the list. If the second element of the list is smaller, I cons the second element with the result of sorting the first two elements of the cons of the first element and everything after the second element (sorry if that's hard to follow). Then, when there is only one element left in the list, I throw it on the end and return it. However, there is a bug along the way somewhere because I should get the following:
>(sort/predicate < '(8 2 5 2 3))
(2 2 3 5 8)
but instead, I get:
>(sort/predicate < '(8 2 5 2 3))
(2 5 2 3 8)
I'm pretty new to clojure, so any help is greatly appreciated. Also, I would like to keep my code roughly the same (I don't want to use a sorting function that already exists). Thanks
I don't think this is a very efficient way to sort, but I tried to stay true to your intention:
(defn my-sort [cmp-fn [x & xs]]
(cond
(nil? x) '()
(empty? xs) (list x)
:else (let [[y & ys :as s] (my-sort cmp-fn xs)]
(if (cmp-fn x y)
(cons x s)
(cons y (my-sort cmp-fn (cons x ys)))))))
;; merge sort implementation - recursive sort without stack consuming
(defn merge-sort
([v comp-fn]
(if (< (count v) 2) v
(let [[left right] (split-at (quot (count v) 2) v)]
(loop [result []
sorted-left (merge-sort left comp-fn)
sorted-right (merge-sort right comp-fn)]
(cond
(empty? sorted-left) (into result sorted-right)
(empty? sorted-right) (into result sorted-left)
:else (if (comp-fn 0 (compare (first sorted-left) (first sorted-right)))
(recur (conj result (first sorted-left)) (rest sorted-left) sorted-right)
(recur (conj result (first sorted-right)) sorted-left (rest sorted-right))))))))
([v] (merge-sort v >)))
clojure.core/sort implement by Java more general.
user=> (sort '(8 2 5 2 3))
(2 2 3 5 8)
user=> (sort > '(8 2 5 2 3))
(8 5 3 2 2)
user=> (source sort)
(defn sort
"Returns a sorted sequence of the items in coll. If no comparator is
supplied, uses compare. comparator must implement
java.util.Comparator. If coll is a Java array, it will be modified.
To avoid this, sort a copy of the array."
{:added "1.0"
:static true}
([coll]
(sort compare coll))
([^java.util.Comparator comp coll]
(if (seq coll)
(let [a (to-array coll)]
(. java.util.Arrays (sort a comp))
(seq a))
())))
nil
user=>