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.
Related
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.
I am trying to make a guess the number game in clojure but I keep getting an error saying I can only recur from tail position
(def n (rand-int 100))
(prn n)
(println "You have 10 guesses :D")
(println "HINT: My number is between 1 and 100")
(dotimes [i 10]
(def guess (read-line))
(if (= guess str(n))
(recur (println "Correct!") (println "Incorrect"))))
(I am new to clojure)
dotimes is used to execute the body for sideeffects that exact amount given; there is no means to break - except throwing
loop (or functions) are recur targets. Next you would have to count down the attempts so you can stop, if the user did not guess it:
(loop [attempts 10]
; ...
(recur (dec attempts)))
There are also other problematic things:
Don't def inside other forms. Use let instead.
str(n) will throw, as it will try to call n (ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn)
recuring with println looks fishy, since println returns always nil
How do you end dotimes? You don't. Try using loop instead. There are a lot of issues with your code but that's a start.
though this is discouraged and counterclojurish to even think of short circuiting the execution this way, it is still totally possible with macros (purely for education and fun)
(defmacro return [& x]
`(list '~'return (do ~#x)))
(defmacro dotimes+ [[i n] & body]
`(loop [~i 0 res# nil]
(cond (and (list? res#) (= '~'return (first res#))) (second res#)
(< ~i ~n) (recur (inc ~i) (do ~#body))
:else res#)))
can be used like this:
user> (dotimes+ [i 10]
(println i)
(if (== i 5) (return :short-circuited)))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
:short-circuited
user> (dotimes+ [i 10]
(println i)
(if (== i 5) (return)))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
nil
user> (dotimes+ [i 10]
(println i))
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
;; 6
;; 7
;; 8
;; 9
nil
notice, that it still expects the return macro to be called in tail position (similar to recur in loop macro)
(dotimes+ [x 4]
(println "attempt" (inc x))
(let [answer (read-line)]
(println "answer is:" answer)
(if (= answer "yes")
(return "YEAH!!!")
(println "WRONG!"))))
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.
If I am returning a lazy-seq from a function like this:
(letfn [(permutations [s]
(lazy-seq
(if (seq (rest s))
(apply concat (for [x s]
(map #(cons x %) (permutations (remove #{x} s)))))
[s])))])
If I use loop recur like below, will the list be eagerly evaluated?
(loop [perms (permutations chain)]
(if (empty? perms)
(prn "finised")
(recur (rest perms))))
If it is eagerly evaluated, can I use loop..recur to lazily loop over what is returned from the permutations function?
The list is lazily evaluated by your loop-recur code.
You can try it out yourself. Let's make permutations print something every time it returns a value by adding a println call.
(defn permutations [s]
(lazy-seq
(if (seq (rest s))
(apply concat (for [x s]
(map #(cons x %) (permutations (remove #{x} s)))))
(do
(println "returning a value")
[s]))))
When using loop, let's also print the values with we're looping over with (prn (first perms).
(loop [perms (permutations [1 2 3])]
(if (empty? perms)
(prn "finised")
(do
(prn (first perms))
(recur (rest perms)))))
Here's what it prints:
returning a value
(1 2 3)
returning a value
(1 3 2)
returning a value
(2 1 3)
returning a value
(2 3 1)
returning a value
(3 1 2)
returning a value
(3 2 1)
"finished"
As you can see, "returning a value" and the value lines are interlaced. The evaluation of the lazy seq can be forced with doall. If you loop over (doall (permutations [1 2 3])), first it prints all the "returning a value" lines and only then the values.
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=>