I just finished 4clojure Problem 60, here's the code for my first program with the problem description:
;;Write a function which behaves like reduce, but returns each intermediate
;;value of the reduction. Your function must accept either two or three arguments,
;;and the return sequence must be lazy.
(fn red
([fun sq]
(red fun (first sq) (rest sq)))
([fun acum sq]
(if (empty? sq) acum
(cons acum (lazy-seq (red fun (fun acum (first sq)) (rest sq)))))))
The core of the function occurs one line bellow the if, I just return the initial value followed by applying it to the next element in the sequence. But it fails for the second test case involving vectors:
user=> (red conj [1] [2 3 4])
([1] [1 2] [1 2 3] 1 2 3 4);; it should be ([1] [1 2] [1 2 3] [1 2 3 4])
It took me some time to realize that the problem was the cons which just adds the vector [1 2 3 4] as it were the rest of the list instead as a single element.
What I did is to convert cons to concat and acum to [acum] and it worked:
(fn red
([fun sq]
(red fun (first sq) (rest sq)))
([fun acum sq]
(if (empty? sq) [acum]
(concat [acum] (lazy-seq
(red fun (fun acum (first sq)) (rest sq)))))))
Don't ask me why but it seems to me kind of inelegant, the other solutions didn't use concat neither.
The question is, considering the first function as it is, what function/macro does the work without modifying the code too much.
In the if true case return [acum] instead of acum
You can avoid the use of concat if you take cons instead. Cons is lazy (see the discussion of cons vs. conj on Stack Overflow):
(defn my-reductions
([f sq] (my-reductions f (first sq) (rest sq)))
([f init sq]
(if (empty? sq)
(list init)
(cons init (lazy-seq (my-reductions f (f init (first sq)) (rest sq))))
)
)
)
Btw: This code passes the 4Clojure tests, but doesn't quite behave like reductions in all cases:
(my-reductions + [])
(nil)
(reductions + [])
(0)
Related
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 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.
I know that there are multiple ways to solve permutations using Clojure.
I have tried creating a DCG (definite clause grammar) using Core.Logic but
the DCG part of the library is too experimental and didn't work.
In the code below I try two different approaches. One is a list comprehension (commented out), which is similar to the way I would solve this problem in Haskell.
The second approach uses MapCat to apply cons/first to each return value from the
recursive call to permutation. Remove item makes sure that I don't use the same letter more than once for each position.
Can someone please explain what is wrong with the list comprehension approach and what is wrong with the MapCat approach. It is much easier to reason about this kind of problem in Haskell - is there some perspective I am missing about Clojure?
(defn remove-item [xs]
(remove #{(first xs)} xs )
)
(defn permutation [xs]
(if (= (count xs) 1)
xs
;(for [x xs y (permutation (remove-item xs))
; :let [z (map concat y)]]
; z)
(mapcat #(map cons first (permutation (remove-item %)) ) xs)
)
)
Edit: #thumbnail solved the MapCat sub-problem in the comments already
We can simplify the permutation function to
(defn permutation [xs]
(if (= (count xs) 1)
xs
(for [x xs
y (permutation (remove-item xs))]
(map concat y))))
Attempting to use it on anything plural produces java.lang.IllegalArgumentException: Don't know how to create ISeq from: ... whatever you are trying to permute.
There are two errors:
permutation should return a sequence of sequences, even when there is
only one of them; so xs should be (list xs). This is what causes the exception.
The permutation for a given x from xs and, given that, a permutation y of xs without xis just (cons x y).
With these corrected, we have
(defn permutation [xs]
(if (= (count xs) 1)
(list xs)
(for [x xs
y (permutation (remove-item x xs))]
(cons x y))))
For example,
(permutation (range 3))
;((0 1 2) (0 2 1) (1 0 2) (1 2 0) (2 0 1) (2 1 0))
The above works only if all the permuted things are different. At the other extreme ...
(permutation [1 1 1])
;()
Also,
count scans the whole of a sequence. To find out if there is only
one element, (seq (rest xs)) is faster than (= (count xs) 1).
And the remove in remove-item scans the whole sequence. There is
little we can do to mend this.
If we know that we are dealing with distinct things, it is simpler and faster to deal with them as a set:
(defn perm-set [xs]
(case (count xs)
0 '()
1 (list (seq xs))
(for [x xs, y (perm-set (disj xs x))]
(cons x y)))
It works for empty sets too.
count is instant and disj is almost constant time, so this is
faster.
Thus:
(perm-set (set '()))
;()
(perm-set (set (range 3)))
;((0 1 2) (0 2 1) (1 0 2) (1 2 0) (2 0 1) (2 1 0))
We can add support for duplicates by working with the index of the items in the original sequence. The function append-index returns a new sequence where the index and value are now in a vector. For example '(\a \b \c) -> '([0 \a] [1 \b] [2 \c] [3 \a]).
You then work with this sequence within the for loop, taking the index of the item when we want to remove it from the original and taking the value when we cons it to the tail sequence.
(defn remove-nth [coll n]
(into (drop (inc n) coll) (reverse (take n coll))))
(defn append-index [coll]
(map-indexed #(conj [%1] %2) coll))
(defn permutation [xs]
(let [i-xs (append-index xs)]
(if (= (count xs) 1)
(list xs)
(for [x i-xs
y (permutation (remove-nth xs (first x)))]
(cons (last x) y)))))
Thanks to the previous post, I was struggling with the permutation problem myself and had not considered using a for comprehension.
I've been working through problems on 4Clojure today, and I ran into trouble on Problem 28, implementing flatten.
There are a couple of definite problems with my code.
(fn [coll]
((fn flt [coll res]
(if (empty? coll)
res
(if (seq? (first coll))
(flt (into (first coll) (rest coll)) res)
(flt (rest coll) (cons (first coll) res))))) coll (empty coll)))
I could use some pointers on how to think about a couple of problems.
How do I make sure I'm not changing the order of the resulting list? cons and conj both add elements wherever it is most efficient to add elements (at the beginning for lists, at the end for vectors, etc), so I don't see how I'm supposed to have any control over this when working with a generic sequence.
How do I handle nested sequences of different types? For instance, an input of '(1 2 [3 4]) will will output ([3 4] 2 1), while an input of [1 2 '(3 4)] will output (4 3 2 1)
Am I even approaching this from the 'right' angle? Should I use a recursive inner function with an accumulator to do this, or am I missing something obvious?
You should try to use HOF (higher order functions) as much as possible: it communicates your intent more clearly and it spares you from introducing subtle low-level bugs.
(defn flatten [coll]
(if (sequential? coll)
(mapcat flatten coll)
(list coll)))
Regarding your questions about lists and vectors. As you might see in tests, output is list. Just make correct abstraction. Fortunately, clojure already has one, called sequence.
All you need is first, rest and some recursive solution.
One possible approach:
(defn flatten [[f & r]]
(if (nil? f)
'()
(if (sequential? f)
(concat (flatten f) (flatten r))
(cons f (flatten r)))))
Here's how to do it in a tail call optimised way, within a single iteration, and using the least amount of Clojure.core code as I could:
#(loop [s % o [] r % l 0]
(cond
(and (empty? s) (= 0 l))
o
(empty? s)
(recur r
o
r
(dec l))
(sequential? (first s))
(recur (first s)
o
(if (= 0 l)
(rest s)
r)
(inc l))
:else
(recur (rest s)
(conj o (first s))
r
l)))
I'm reading Programming Clojure now and I find out next example
(defn by-pairs [coll]
(let
[take-pair (fn [c] (when (next c) (take 2 c)))]
(lazy-seq
(when-let [pair (seq (take-pair coll))] ;seq calls here
(cons pair (by-pairs (rest coll)))))))
it breaks list into pairs, like
(println (by-pairs [1 2 1])) ((1 2) (2 1))
(println (by-pairs [1 2 1 3])) ((1 2) (2 1) (1 3))
(println (by-pairs [])) ()
(println (by-pairs [1])) ()
What I can not get is why we should invoke seq on take-pair result? So why we can not just write
(defn by-pairs [coll]
(let
[take-pair (fn [c] (when (next c) (take 2 c)))]
(lazy-seq
(when-let [pair (take-pair coll)]
(cons pair (by-pairs (rest coll)))))))
In witch cases there are will be different results or are there are any performance reasons?
Both the code are same and there will be no difference because next and take functions that are being applied to coll in take-pair function, do call seq on the passed parameter i.e next c will first call seq on c or try to check if it is an object which implements ISeq and same in being doing by the take function. So basically in this case if you don't call seq yourself, the next and take will call seq on it.