I was reading the ebook Functional Programming Patterns in Scala & Clojure and found a code sample that led to this question.
This piece of code is meant to compare two Person objects. The comparision algo is - First compare their FNames, if equal then compare their LName, if equal then compare their MNames.
Clojure code as given in the book (more or less)
(def person1 {:fname "John" :mname "Q" :lname "Doe"})
(def person2 {:fname "Jane" :mname "P" :lname "Doe"})
(defn fname-compare [p1 p2]
(do
(println "Comparing fname")
(compare (:fname p1) (:fname p2))))
(defn lname-compare [p1 p2]
(do
(println "Comparing lname")
(compare (:lname p1) (:lname p2))))
(defn mname-compare [p1 p2]
(do
(println "Comparing mname")
(compare (:mname p1) (:mname p2))))
(defn make-composed-comparison [& comparisons]
(fn [p1 p2]
(let [results (for [comparison comparisons] (comparison p1 p2))
first-non-zero-result
(some (fn [result] (if (not (= 0 result)) result nil)) results)]
(if (nil? first-non-zero-result)
0
first-non-zero-result))))
(def people-comparision-1
(make-composed-comparison fname-compare lname-compare mname-compare))
(people-comparision-1 person1 person2)
;Output
;Comparing fname
;Comparing lname
;Comparing mname
;14
Thing is, as per this sample it will do all three comparisons even if the first one returned non zero. In this case its not an issue. However if I had written idiomatic C# code, then that code would have done only one comparison and exited. Sample C# code
public class Person {
public string FName {get; set;}
public string LName {get; set;}
public string MName {get; set;}
}
var comparators =
new List<Func<Person, Person, int>> {
(p1, p1) => {
Console.WriteLine("Comparing FName");
return string.Compare(p1.FName, p2.FName);
},
(p1, p1) => {
Console.WriteLine("Comparing LName");
return string.Compare(p1.LName, p2.LName);
},
(p1, p1) => {
Console.WriteLine("Comparing MName");
return string.Compare(p1.MName, p2.MName);
}
};
var p1 = new Person {FName = "John", MName = "Q", LName = "Doe"};
var p2 = new Person {FName = "Jane", MName = "P", LName = "Doe"};
var result =
comparators
.Select(x => x(p1, p2))
.Where(x => x != 0)
.FirstOrDefault();
Console.WriteLine(result);
// Output
// Comparing FName
// 1
A naive translation of the above code into clojure gives me
(defn compose-comparators [& comparators]
(fn [x y]
(let [result
(->> comparators
(map #(% x y))
(filter #(not (zero? %)))
first)]
(if (nil? result)
0
result))))
(def people-comparision-2
(compose-comparators fname-compare lname-compare mname-compare))
(people-comparision-2 person1 person2)
;Output
;Comparing fname
;Comparing lname
;Comparing mname
;14
And that is not what I expected. I read somewhere that clojure processes 32 elements of a sequence at a time for performance reasons or something. What is the idiomatic Clojure way to get the output/behaviour similar to the C# code?
The following is my attempt. However it doesn't feel "clojurey".
(defn compose-comparators-2 [& comparators]
(fn [x y]
(loop [comparators comparators
result 0]
(if (not (zero? result))
result
(let [comparator (first comparators)]
(if (nil? comparator)
0
(recur (rest comparators) (comparator x y))))))))
(def people-comparision-3
(compose-comparators-2 fname-compare lname-compare mname-compare))
(people-comparision-3 person1 person2)
;Output
;Comparing fname
;14
Edit :
Based on answers to this question as well as the answer to a related question, I think if I need early exit, I should be explicit about it. One way would be to convert the collection to a lazy one. The other option is to use reduced to exit early from the reduce loop.
With the knowledge I currently have, I am more inclined to opt for the explicit lazy collection route. Is there an issue with using the following function to do so -
(defn lazy-coll [coll]
(lazy-seq
(when-let [s (seq coll)]
(cons (first s) (lazy-coll (rest s))))))
This way I can use map, remove the way I normally would have.
As you suspected yourself and as the other answers have noted, the problem is that chunked sequences are not as lazy as they might be.
If we look at your compose-comparators function (slightly simplified)
(defn compose-comparators [& comparators]
(fn [x y]
(let [result (->> comparators
(map #(% x y))
(remove zero?)
first)]
(if (nil? result) 0 result))))
... the reason that all three comparisons are run in people-comparison-2 is that map deals with a chunked sequence in chunks, as you can see here.
A simple solution is to substitute a map with the chunkiness removed:
(defn lazy-map [f coll]
(lazy-seq
(when-let [s (seq coll)]
(cons (f (first s)) (lazy-map f (rest s))))))
By the way, you can abstract the construction of the comparator functions. If we define
(defn comparer [f]
(fn [x y]
(println "Comparing with " f)
(compare (f x) (f y))))
... we can use it to define
(def people-comparision-2
(apply compose-comparators (map comparer [:fname :lname :mname])))
I did some tests with your code and it happens that:
((compose-comparators fname-compare lname-compare mname-compare) person1 person2)
Does work as intended and compares fname only.
According to this blog post, laziness in Clojure might not be enforced as strictly as we might imagine:
Clojure as a language is not lazy by default in totality (unlike
Haskell) and hence laziness may get mixed up with strict evaluation
leading to surprising and unoptimized consequences.
I think you're running into chunked sequences. I'm not an expert on this, but my understanding is that depending on the type of sequence you have, clojure may evaluate it in chunks of 32 elements rather than being fully lazy.
e.g. Your first code (that didn't work as hoped) is effectively:
;; I renamed your compare fns to c1, c2, c3
(->> [c1 c2 c3] ; vector; will be chunked
(map #(% person1 person2))
(filter #(not (zero? %)))
first)
comparing fname
comparing lname
comparing mname
14
versus
(->> (list c1 c2 c3) ; list i.e. (cons c1 (cons c2 (cons c3 nil)))
(map #(% person1 person2))
(filter #(not (zero? %)))
first)
;comparing fname
;14
Given this unfortunate behaviour you might want to try another approach. How about this:
(fixed version based on Amith George's comment below)
(some (fn [f]
(let [result (f person1 person2)]
(if (zero? result) false result)))
[c1 c2 c3])
;comparing fname
;14
We do, in fact, have something close to yield. It is called reduced.
It is only incidental that a lazy-seq avoids calculation of results, there is no strict guarantee that a result not used is not evaluated. This is beneficial for performance (it is often faster to calculate a chunk of results from a lazy-seq at once rather than one at a time).
What we want here is not laziness of results, but short circuiting if a specific result is found, and that is what reduced is designed for.
(defn compare-by-key
[k]
(fn [p1 p2]
(println "Comparing" (name k))
(compare (k p1) (k p2))))
(def fname-compare (compare-by-key :fname))
(def lname-compare (compare-by-key :lname))
(def mname-compare (compare-by-key :mname))
(defn make-composed-comparison [& comparisons]
(fn [p1 p2]
(or (reduce (fn [_ comparison]
(let [compared (comparison p1 p2)]
(when-not (zero? compared)
(reduced compared))))
false
comparisons)
0)))
(def people-comparison-1
(make-composed-comparison fname-compare lname-compare mname-compare))
I also attempted to make a few things more idiomatic here, hopefully your original code is still recognizable.
user> (people-comparison-1 person1 person2)
Comparing fname
14
Related
I am new to Clojure, and doing my best to forget all my previous experience with more procedural languages (java, ruby, swift) and embrace Clojure for what it is. I am actually really enjoying the way it makes me think differently -- however, I have come up against a pattern that I just can't seem to figure out. The easiest way to illustrate, is with some code:
(defn char-to-int [c] (Integer/valueOf (str c)))
(defn digits-dont-decrease? [str]
(let [digits (map char-to-int (seq str)) i 0]
(when (< i 5)
(if (> (nth digits i) (nth digits (+ i 1)))
false
(recur (inc i))))))
(def result (digits-dont-decrease? "112233"))
(if (= true result)
(println "fit rules")
(println "doesn't fit rules"))
The input is a 6 digit number as a string, and I am simply attempting to make sure that each digit from left to right is >= the previous digit. I want to return false if it doesn't, and true if it does. The false situation works great -- however, given that recur needs to be the last thing in the function (as far as I can tell), how do I return true. As it is, when the condition is satisfied, I get an illegal argument exception:
Execution error (IllegalArgumentException) at clojure.exercise.two/digits-dont-decrease? (four:20).
Don't know how to create ISeq from: java.lang.Long
How should I be thinking about this? I assume my past training is getting in my mental way.
This is not answering your question, but also shows an alternative. While the (apply < ...) approach over the whole string is very elegant for small strings (it is eager), you can use every? for an short-circuiting approach. E.g.:
user=> (defn nr-seq [s] (map #(Integer/parseInt (str %)) s))
#'user/nr-seq
user=> (every? (partial apply <=) (partition 2 1 (nr-seq "123")))
true
You need nothing but
(apply <= "112233")
Reason: string is a sequence of character and comparison operator works on character.
(->> "0123456789" (mapcat #(repeat 1000 %)) (apply str) (def loooong))
(count loooong)
10000
(time (apply <= loooong))
"Elapsed time: 21.006625 msecs"
true
(->> "9123456789" (mapcat #(repeat 1000 %)) (apply str) (def bad-loooong))
(count bad-loooong)
10000
(time (apply <= bad-loooong))
"Elapsed time: 2.581750 msecs"
false
(above runs on my iPhone)
In this case, you don't really need loop/recur. Just use the built-in nature of <= like so:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(def true-samples
["123"
"112233"
"13"])
(def false-samples
["10"
"12324"])
(defn char->int
[char-or-str]
(let [str-val (str char-or-str)] ; coerce any chars to len-1 strings
(assert (= 1 (count str-val)))
(Integer/parseInt str-val)))
(dotest
(is= 5 (char->int "5"))
(is= 5 (char->int \5))
(is= [1 2 3] (mapv char->int "123"))
; this shows what we are going for
(is (<= 1 1 2 2 3 3))
(isnt (<= 1 1 2 1 3 3))
and now test the char sequences:
;-----------------------------------------------------------------------------
; using built-in `<=` function
(doseq [true-samp true-samples]
(let [digit-vals (mapv char->int true-samp)]
(is (apply <= digit-vals))))
(doseq [false-samp false-samples]
(let [digit-vals (mapv char->int false-samp)]
(isnt (apply <= digit-vals))))
if you want to write your own, you can like so:
(defn increasing-equal-seq?
"Returns true iff sequence is non-decreasing"
[coll]
(when (< (count coll) 2)
(throw (ex-info "coll must have at least 2 vals" {:coll coll})))
(loop [prev (first coll)
remaining (rest coll)]
(if (empty? remaining)
true
(let [curr (first remaining)
prev-next curr
remaining-next (rest remaining)]
(if (<= prev curr)
(recur prev-next remaining-next)
false)))))
;-----------------------------------------------------------------------------
; using home-grown loop/recur
(doseq [true-samp true-samples]
(let [digit-vals (mapv char->int true-samp)]
(is (increasing-equal-seq? digit-vals))))
(doseq [false-samp false-samples]
(let [digit-vals (mapv char->int false-samp)]
(isnt (increasing-equal-seq? digit-vals))))
)
with result
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 2 tests containing 15 assertions.
0 failures, 0 errors.
Passed all tests
Finished at 23:36:17.096 (run time: 0.028s)
You an use loop with recur.
Assuming you require following input v/s output -
"543221" => false
"54321" => false
"12345" => true
"123345" => true
Following function can help
;; Assuming char-to-int is defined by you before as per the question
(defn digits-dont-decrease?
[strng]
(let [digits (map char-to-int (seq strng))]
(loop [;;the bindings in loop act as initial state
decreases true
i (- (count digits) 2)]
(let [decreases (and decreases (>= (nth digits (+ i 1)) (nth digits i)))]
(if (or (< i 1) (not decreases))
decreases
(recur decreases (dec i)))))))
This should work for numeric string of any length.
Hope this helps. Please let me know if you were looking for something else :).
(defn non-decreasing? [str]
(every?
identity
(map
(fn [a b]
(<= (int a) (int b)))
(seq str)
(rest str))))
(defn non-decreasing-loop? [str]
(loop [a (seq str) b (rest str)]
(if-not (seq b)
true
(if (<= (int (first a)) (int (first b)))
(recur (rest a) (rest b))
false))))
(non-decreasing? "112334589")
(non-decreasing? "112324589")
(non-decreasing-loop? "112334589")
(non-decreasing-loop? "112324589")
We have to develop a poker game. I have developed all the required functions but I'm stuck with one. It goes: (higher-kicker? kicker1 kicker2) compares the corresponding values in the two kickers, and returns true if the first kicker has the larger value of the first difference, false if the second kicker does, or if the lists are pairwise equal. Example: (higher-kicker? '(8 5 9) '(8 7 3)) should return false, because 8==8 but 7>5. Assume that the two kicker lists are of equal lengths.
What I've been able to do is compare the two hands, like:
(defn compare-cards [[v1 s1] [v2 s2]]
(if (= v1 v2)
(compare (suit-value s1) (suit-value s2))
(compare v1 v2)))
(defn sort-cards [cards]
(sort compare-cards cards))
(defn parse-hand [s]
(sort-cards (mapv parse-card (.split s " "))))
(def foo [[:straight straight?] [:high-card high-card?]])
(defn categorize-hand [hand]
(some #(% (parse-hand hand)) (map second foo)))
(defmulti tie-break (fn [h _] (categorize-hand h)))
(defmethod tie-break :high-card [h1 h2]
(drop-while zero? (map compare-cards (reverse h1) (reverse h2))))
(defmethod tie-break :straight [[f1 & _] [f2 & _]]
(compare-cards f1 f2))
(defn compare-hands [hand1 hand2]
(let [category1-value (.indexOf (map first foo) (categorize-hand hand1))
category2-value (.indexOf (map first foo) (categorize-hand hand2))]
(if (= category1-value category2-value)
(tie-break (parse-hand hand1) (parse-hand hand2))
(compare category1-value category2-value))))
But, Im stuck when it comes to comparing the face values one by one to see if the first one is greater. Can anyone help me?
Like I'm doing:
(defn higher-kicker? [
card-ranks-1 card-ranks-2]
(->> (map compare card-ranks-1 card-ranks-2)
(filter #(not (zero? %)))
then what to do after that?
Oddly enough I could not find a function that creates a list of pairs from two lists so I rolled my own. Beware zipmap because it does not preserve ordering. That said after that it's all rather simple. Get the first unequal pair. If there aren't any the lists are equal so return false otherwise compare them and return if the first is greater than the second.
(defn make-pairs [list1 list2] (partition 2 (interleave list1 list2)))
(defn pair-not= [[item1 item2]] (not (= item1 item2)))
(defn unequal-pairs [list1 list2] (filter pair-not= (make-pairs list1 list2)))
(defn higher-kicker? [kicker1 kicker2]
(let [unequal-pair (first (unequal-pairs kicker1 kicker2))]
(if unequal-pair
(> (first unequal-pair) (second unequal-pair))
false)))
4Clojure Problem 58 is stated as:
Write a function which allows you to create function compositions. The parameter list should take a variable number of functions, and create a function applies them from right-to-left.
(= [3 2 1] ((__ rest reverse) [1 2 3 4]))
(= 5 ((__ (partial + 3) second) [1 2 3 4]))
(= true ((__ zero? #(mod % 8) +) 3 5 7 9))
(= "HELLO" ((__ #(.toUpperCase %) #(apply str %) take) 5 "hello world"))
Here __ should be replaced by the solution.
In this problem the function comp should not be employed.
A solution I found is:
(fn [& xs]
(fn [& ys]
(reduce #(%2 %1)
(apply (last xs) ys) (rest (reverse xs)))))
It works. But I don't really understand how the reduce works here. How does it represent (apply f_1 (apply f_2 ...(apply f_n-1 (apply f_n args))...)?
Let's try modifying that solution in 3 stages. Stay with each for a while and see if you get it. Stop if and when you do lest I confuse you more.
First, let's have more descriptive names
(defn my-comp [& fns]
(fn [& args]
(reduce (fn [result-so-far next-fn] (next-fn result-so-far))
(apply (last fns) args) (rest (reverse fns)))))
then factor up some
(defn my-comp [& fns]
(fn [& args]
(let [ordered-fns (reverse fns)
first-result (apply (first ordered-fns) args)
remaining-fns (rest ordered-fns)]
(reduce
(fn [result-so-far next-fn] (next-fn result-so-far))
first-result
remaining-fns))))
next replace reduce with a loop which does the same
(defn my-comp [& fns]
(fn [& args]
(let [ordered-fns (reverse fns)
first-result (apply (first ordered-fns) args)]
(loop [result-so-far first-result, remaining-fns (rest ordered-fns)]
(if (empty? remaining-fns)
result-so-far
(let [next-fn (first remaining-fns)]
(recur (next-fn result-so-far), (rest remaining-fns))))))))
My solution was:
(fn [& fs]
(reduce (fn [f g]
#(f (apply g %&))) fs))
Lets try that for:
((
(fn [& fs]
(reduce (fn [f g]
#(f (apply g %&))) fs))
#(.toUpperCase %)
#(apply str %)
take)
5 "hello world"))
fs is a list of the functions:
#(.toUpperCase %)
#(apply str %)
take
The first time through the reduce, we set
f <--- #(.toUpperCase %)
g <--- #(apply str %)
We create an anonymous function, and assign this to the reduce function's accumulator.
#(f (apply g %&)) <---- uppercase the result of apply str
Next time through the reduce, we set
f <--- uppercase the result of apply str
g <--- take
Again we create a new anonymous function, and assign this to the reduce function's accumulator.
#(f (apply g %&)) <---- uppercase composed with apply str composed with take
fs is now empty, so this anonymous function is returned from reduce.
This function is passed 5 and "hello world"
The anonymous function then:
Does take 5 "hello world" to become (\h \e \l \l \o)
Does apply str to become "hello"
Does toUppercase to become "HELLO"
Here's an elegent (in my opinion) definition of comp:
(defn comp [& fs]
(reduce (fn [result f]
(fn [& args]
(result (apply f args))))
identity
fs))
The nested anonymous functions might make it hard to read at first, so let's try to address that by pulling them out and giving them a name.
(defn chain [f g]
(fn [& args]
(f (apply g args))))
This function chain is just like comp except that it only accepts two arguments.
((chain inc inc) 1) ;=> 3
((chain rest reverse) [1 2 3 4]) ;=> (3 2 1)
((chain inc inc inc) 1) ;=> ArityException
The definition of comp atop chain is very simple and helps isolate what reduce is bringing to the show.
(defn comp [& fs]
(reduce chain identity fs))
It chains together the first two functions, the result of which is a function. It then chains that function with the next, and so on.
So using your last example:
((comp #(.toUpperCase %) #(apply str %) take) 5 "hello world") ;=> "HELLO"
The equivalent only using chain (no reduce) is:
((chain identity
(chain (chain #(.toUpperCase %)
#(apply str %))
take))
5 "hello world")
;=> "HELLO"
At a fundamental level, reduce is about iteration. Here's what an implementation in an imperative style might look like (ignoring the possibility of multiple arities, as Clojure's version supports):
def reduce(f, init, seq):
result = init
for item in seq:
result = f(result, item)
return result
It's just capturing the pattern of iterating over a sequence and accumulating a result. I think reduce has a sort of mystique around it which can actually make it much harder to understand than it needs to be, but if you just break it down you'll definitely get it (and probably be surprised how often you find it useful).
Here is my solution:
(defn my-comp
([] identity)
([f] f)
([f & r]
(fn [& args]
(f (apply (apply my-comp r) args)))))
I like A. Webb's solution better, though it does not behave exactly like comp because it does not return identity when called without any arguments. Simply adding a zero-arity body would fix that issue though.
Consider this example:
(def c (comp f1 ... fn-1 fn))
(c p1 p2 ... pm)
When c is called:
first comp's rightmost parameter fn is applied to the p* parameters ;
then fn-1 is applied to the result of the previous step ;
(...)
then f1 is applied to the result of the previous step, and its result is returned
Your sample solution does exactly the same.
first the rightmost parameter (last xs) is applied to the ys parameters:
(apply (last xs) ys)
the remaining parameters are reversed to be fed to reduce:
(rest (reverse xs))
reduce takes the provided initial result and list of functions and iteratively applies the functions to the result:
(reduce #(%2 %1) ..init.. ..functions..)
Being quite new to clojure I am still struggling with its functions. If I have 2 lists, say "1234" and "abcd" I need to make all possible ordered lists of length 4. Output I want to have is for length 4 is:
("1234" "123d" "12c4" "12cd" "1b34" "1b3d" "1bc4" "1bcd"
"a234" "a23d" "a2c4" "a2cd" "ab34" "ab3d" "abc4" "abcd")
which 2^n in number depending on the inputs.
I have written a the following function to generate by random walk a single string/list.
The argument [par] would be something like ["1234" "abcd"]
(defn make-string [par] (let [c1 (first par) c2 (second par)] ;version 3 0.63 msec
(apply str (for [loc (partition 2 (interleave c1 c2))
:let [ch (if (< (rand) 0.5) (first loc) (second loc))]]
ch))))
The output will be 1 of the 16 ordered lists above. Each of the two input lists will always have equal length, say 2,3,4,5, up to say 2^38 or within available ram. In the above function I have tried to modify it to generate all ordered lists but failed. Hopefully someone can help me. Thanks.
Mikera is right that you need to use recursion, but you can do this while being both more concise and more general - why work with two strings, when you can work with N sequences?
(defn choices [colls]
(if (every? seq colls)
(for [item (map first colls)
sub-choice (choices (map rest colls))]
(cons item sub-choice))
'(())))
(defn choose-strings [& strings]
(for [chars (choices strings)]
(apply str chars)))
user> (choose-strings "123" "abc")
("123" "12c" "1b3" "1bc" "a23" "a2c" "ab3" "abc")
This recursive nested-for is a very useful pattern for creating a sequence of paths through a "tree" of choices. Whether there's an actual tree, or the same choice repeated over and over, or (as here) a set of N choices that don't depend on the previous choices, this is a handy tool to have available.
You can also take advantage of the cartesian-product from the clojure.math.combinatorics package, although this requires some pre- and post-transformation of your data:
(ns your-namespace (:require clojure.math.combinatorics))
(defn str-combinations [s1 s2]
(->>
(map vector s1 s2) ; regroup into pairs of characters, indexwise
(apply clojure.math.combinatorics/cartesian-product) ; generate combinations
(map (partial apply str)))) ; glue seqs-of-chars back into strings
> (str-combinations "abc" "123")
("abc" "ab3" "a2c" "a23" "1bc" "1b3" "12c" "123")
>
The trick is to make the function recursive, calling itself on the remainder of the list at each step.
You can do something like:
(defn make-all-strings [string1 string2]
(if (empty? string1)
[""]
(let [char1 (first string1)
char2 (first string2)
following-strings (make-all-strings (next string1) (next string2))]
(concat
(map #(str char1 %) following-strings)
(map #(str char2 %) following-strings)))))
(make-all-strings "abc" "123")
=> ("abc" "ab3" "a2c" "a23" "1bc" "1b3" "12c" "123")
(defn combine-strings [a b]
(if (seq a)
(for [xs (combine-strings (rest a) (rest b))
x [(first a) (first b)]]
(str x xs))
[""]))
Now that I wrote it I realize it's a less generic version of amalloiy's one.
You could also use the binary digits of numbers between 0 and 16 to form your combinations:
if a bit is zero select from the first string otherwise the second.
E.g. 6 = 2r0110 => "1bc4", 13 = 2r1101 => "ab3d", etc.
(map (fn [n] (apply str (map #(%1 %2)
(map vector "1234" "abcd")
(map #(if (bit-test n %) 1 0) [3 2 1 0])))); binary digits
(range 0 16))
=> ("1234" "123d" "12c4" "12cd" "1b34" "1b3d" "1bc4" "1bcd" "a234" "a23d" "a2c4" "a2cd" "ab34" "ab3d" "abc4" "abcd")
The same approach can apply to generating combinations from more than 2 strings.
Say you have 3 strings ("1234" "abcd" "ABCD"), there will be 81 combinations (3^4). Using base-3 ternary digits:
(defn ternary-digits [n] (reverse (map #(mod % 3) (take 4 (iterate #(quot % 3) n))))
(map (fn [n] (apply str (map #(%1 %2)
(map vector "1234" "abcd" "ABCD")
(ternary-digits n)
(range 0 81))
(def c1 "1234")
(def c2 "abcd")
(defn make-string [c1 c2]
(map #(apply str %)
(apply map vector
(map (fn [col rep]
(take (math/expt 2 (count c1))
(cycle (apply concat
(map #(repeat rep %) col)))))
(map vector c1 c2)
(iterate #(* 2 %) 1)))))
(make-string c1 c2)
=> ("1234" "a234" "1b34" "ab34" "12c4" "a2c4" "1bc4" "abc4" "123d" "a23d" "1b3d" "ab3d" "12cd" "a2cd" "1bcd" "abcd")
I'm resolving a 4Clojure exercise, this exercise asks you to build your own interpose function. My answer follows:
(fn my-interpose
([separator input] (my-interpose separator input nil))
([separator input result]
(if
(empty? input)
(reverse (rest result))
(my-interpose separator (rest input) (cons separator (cons (first input) result))))))
I'm doing these exercises to learn the language as I read a Clojure book. I will like to know the opinion about my code of people with an experience in the language. Could I avoid the reverse call? Are there any conventions I'm breaking hardly with this kind of code?
What you have is a good proper starting point :). Excellent work.
Starting with what you have you may want to:
Replace your recursive call with a call to recur because as written it will hit a stack overflow
(defn foo [stuff]
(dostuff ... )
(foo (rest stuff)))
becomes:
(defn foo [stuff]
(dostuff ...)
(recur (rest stuff)))
to avoid blowing the stack. this then often becomes:
(map dostuff stuff)
Replace the recustion entirely with the for function
(for [a one-list b another-list]
(dont-give-away-the-answer))
Yes you can avoid the reverse call,
(defn my-interpose [sep coll]
(when (seq coll)
(lazy-cat [(first coll) sep]
(my-interpose sep (rest coll)))))
as arthur suggest you can use recur in order to not to blow the stack but 99% of the time you don't need it.
EDIT:
This is a bit cleaner,
(defn my-interpose [sep coll]
(if-let [[first & rest] coll]
(lazy-cat [first sep] (my-interpose sep rest))))
Got to various answers using mapcat and for but in the end I found this:
#(rest (interleave (repeat %1) %2))
Where the first argument is the separator and the second is the collection. Just posting this answer for pure curiosity of other Clojure noobs like me.
Here is my solution, trying to rely on lower-level lisp or scheme-like functions.
(defn my-interpose[sep coll]
(letfn [(f [c]
(when-not (empty? c)
(cons sep (cons (first c)
(f (next c))))))]
(next (f coll))))
You may want to use map
(defn my-interpose [sep coll]
(rest (apply concat (map #(vector sep %) coll))))
or a directly a reduce and compute the answer as you go
(defn my-interpose [sep coll]
(rest (reduce #(conj %1 sep %2) [] coll)))
The idea behind this is to compute a sequence like (sep x0 sep x1 sep x2 ... sep xn) and then skip the first element to get (x0 sep x1 sep x2 ... xn).
Using just reduce and conj:
(defn my-interpose [sep a-seq]
(if (empty? a-seq)
a-seq
(reduce (fn [e1 e2] (if (empty? e1)
(conj e1 e2)
(conj e1 sep e2))) [] a-seq)))