Unexpected behavior of function - clojure

I write the following function to merge s2 into s1, and any element whose "id" value is same as any element in s1 will not be merged.
(defn into-unique-id
[s1, [x2 & xs2]]
(if x2 (if (empty (filter (fn [x] (= (get x "id") (get x2 "id"))) s1)) (into-unique-id (conj s1 x2) xs2) (into-unique-id s1 xs2))
s1))
I tried the following in REPL.
gridizela.core=> (def s1 [{"id" "1"} {"id" "2"}])
#'gridizela.core/s1
gridizela.core=> (into-unique-id s1 [{"id" "1"}])
[{"id" "1"} {"id" "2"} {"id" "1"}]
As you can see, the result was not as I expected, in which the last element should not be added.
Any help would be appreciated.

I had to do a bit of refactoring in order to fix your problem, I am going to include each version of the code to show the steps needed to get the ideal result.
;; original
(defn into-unique-id
[s1, [x2 & xs2]]
(if x2 (if (empty (filter (fn [x] (= (get x "id") (get x2 "id"))) s1)) (into-unique-id (conj s1 x2) xs2) (into-unique-id s1 xs2))
s1))
The original code is unusually laid out, in a way that makes it hard for me to read, and hard for me to understand. So my first step was to apply normal indentation.
;; indentation
(defn into-unique-id-2
[s1, [x2 & xs2]]
(if x2
(if (empty (filter (fn [x] (= (get x "id")
(get x2 "id")))
s1))
(into-unique-id (conj s1 x2) xs2)
(into-unique-id s1 xs2))
s1))
I still didn't quite get the code when I got to this point, but I saw some small changes that would make it easier to read. cond is almost always the thing you want instead of nested if conditionals. I use ,, as whitespace to differentiate the result clauses from the condition clauses in cond.
;; simplification
(defn into-unique-id-3
[s1, [x2 & xs2]]
(cond (not x2)
,, s1
(empty (filter (fn [x] (= (get x "id")
(get x2 "id")))
s1))
,, (into-unique-id (conj s1 x2) xs2)
:else
(into-unique-id s1 xs2)))
At this point I finally saw the error: (empty x) will return truthy for any input that is not nil, even an empty collection. What we actually want here is the deceptively similarly named (but very different) empty? function.
;; fix -- empty is always truthy here
(defn into-unique-id-4
[s1, [x2 & xs2]]
(cond (not x2)
,, s1
(empty? (filter (fn [x] (= (get x "id")
(get x2 "id")))
s1))
,, (into-unique-id (conj s1 x2) xs2)
:else
(into-unique-id s1 xs2)))
next I saw that instead of filter / empty? we can use the built in some.
;; simplification -- we have idiomatic functions for this
(defn into-unique-id-5
[s1, [x2 & xs2]]
(cond (not x2)
,, s1
(some (fn [x] (= (get x "id")
(get x2 "id")))
s1)
,, (into-unique-id s1 xs2)
:else
,, (into-unique-id (conj s1 x2) xs2)))
Early on I noticed this was effectively a reduce done by hand, and so as a last step I'll show the function as a reduce.
;; structural simplification - we have idiomatic higher order functions
;; that we can use instead of recursion
(defn into-unique-id-6
[s1, coll]
(reduce
(fn [acc el]
(if (some (fn [x]
(= (get x "id")
(get el "id")))
acc)
acc
(conj acc el)))
s1
coll))

Related

How to iterate through a list and make lists of the elements

I am trying to convert logical functions in clojure. I want the user to be able to type in (convert '(and x y z) to produce (nor (nor x) (nor y) (nor z). So I am creating a list with first element nor, and then trying to make the rest of the elements lists that are created when going through a for loop. However the for loop just combines all the lists, and keeps the nor outside of it. I also want to know how to skip the first element in the list but that's not my priority right now. I'm kinda new to clojure and can't figure out how to just return all of the lists to be put into the bigger list. The not and or function aren't related to the problem.
(defn lookup
"Look up a value, i, in map m and returns the result if it exists.
Otherwise returns i."
[i m]
(get m i i))
(defn makelist
[l]
(for[i[l]] (list 'nor i)))
(defn convert
[l]
(let [p1 (first l)]
(cond
(= p1 'not) (map (fn [i] (lookup i '{not nor})) l)
(= p1 'or) (list 'nor (map(fn [i] (lookup i '{or nor})) l))
(= p1 'and) (list 'nor (makelist l))
:else (print "error"))))
The output I get is (nor ((nor (and x y z)))). The output I want is (nor (nor and) (nor x) (nor y) (nor z). I don't want the (nor and) either but until I can figure out how to skip the first element I just want to be able to separate the lists out.
There are two problems that I can see:
makelist has (for [i [l]] ...) so it only produces a single item where i is bound to the whole of the incoming list l -- what you want here is (for [i l] ...) so that each element of l is processed,
convert's clause for and creates a list with two elements: nor and the result of (makelist l) -- what you want here is (cons 'nor (makelist l)) so that you get a list with nor as the first element and then all of the elements of the result of calling makelist.
I haven't checked the other two parts of convert to see whether you have similar errors, but with the two changes above (convert '(and x y z)) will produce (nor (nor and) (nor x) (nor y) (nor z))
just for fun: i would mentally expand and generalize your task to rewriting data structures according to some rules, so you could declare (possibly recursive) rewrite rules to transform any input to any desired output in general. (and to practice clojure)
let's start with simple conversion function:
(defn convert [rules data]
(if-let [res (some (fn [[condition rewrite]]
(when (condition data) (rewrite data)))
rules)]
res
data))
it finds first rule that suits your input (if any) and applies it's transformation function:
(def my-rules [[sequential? (fn [data] (map #(convert my-rules %) data))]
[number? inc]
[keyword? (comp clojure.string/upper-case name)]])
#'user/my-rules
user> (convert my-rules [:hello :guys "i am" 30 [:congratulate :me]])
;;=> ("HELLO" "GUYS" "i am" 31 ("CONGRATULATE" "ME"))
with this approach, your rules would look something like this:
(def rules
[[(every-pred coll? (comp #{'not} first)) (fn [data] (map (partial convert [[#{'not} (constantly 'nor)]]) data))]
[(every-pred coll? (comp #{'or} first)) (fn [data] (map (partial convert [[#{'or} (constantly 'nor)]]) data))]
[(every-pred coll? (comp #{'and} first)) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
#'user/rules
user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))
ok it works, but looks rather ugly. Still we can elimnate some repetitions introducing couple of basic functions for checkers and transformers:
(defn first-is
"returns a function checking that the input is collection and it's head equals to value"
[value]
(every-pred coll? (comp #{value} first)))
transforming your rules to:
(def rules
[[(first-is 'not) (fn [data] (map (partial convert [[#{'not} (constantly 'nor)]]) data))]
[(first-is 'or) (fn [data] (map (partial convert [[#{'or} (constantly 'nor)]]) data))]
[(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
#'user/rules
user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))
and then introducing replacing rewrite rule:
(defn replacing
([new] [(constantly true) (constantly new)])
([old new] [#{old} (constantly new)]))
leading us to
(def rules
[[(first-is 'not) (fn [data] (map (partial convert [(replacing 'not 'nor)]) data))]
[(first-is 'or) (fn [data] (map (partial convert [(replacing 'or 'nor)]) data))]
[(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
now we can see that there is a demand on a function, transforming every item in collection. let's introduce it:
(defn convert-each [rules]
(fn [data] (map #(convert rules %) data)))
(def rules
[[(first-is 'not) (convert-each [(replacing 'not 'nor)])]
[(first-is 'or) (convert-each [(replacing 'or 'nor)])]
[(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
user> (convert rules '(or x y z))
;;=> (nor x y z)
user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))
now it is much better, but the last clause is still kind of ugly. I can think of introducing the function that transforms head and tail with separate rules and then conses transformed head and tail:
(defn convert-cons [head-rules tail-conversion]
(fn [[h & t]] (cons (convert head-rules h) (tail-conversion t))))
(defn transforming [transformer]
[(constantly true) transformer])
(def rules
[[(first-is 'not) (convert-each [(replacing 'not 'nor)])]
[(first-is 'or) (convert-each [(replacing 'or 'nor)])]
[(first-is 'and) (convert-cons [(replacing 'nor)]
(convert-each [(transforming #(list 'nor %))]))]])
user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))

Make (map f c1 c2) map (count c1) times, even if c2 has less elements

When doing
(map f [0 1 2] [:0 :1])
f will get called twice, with the arguments being
0 :0
1 :1
Is there a simple yet efficient way, i.e. without producing more intermediate sequences etc., to make f get called for every value of the first collection, with the following arguments?
0 :0
1 :1
2 nil
Edit Addressing question by #fl00r in the comments.
The actual use case that triggered this question needed map to always work exactly (count first-coll) times, regardless if the second (or third, or ...) collection was longer.
It's a bit late in the game now and somewhat unfair after having accepted an answer, but if a good answer gets added that only does what I specifically asked for - mapping (count first-coll) times - I would accept that.
You could do:
(map f [0 1 2] (concat [:0 :1] (repeat nil)))
Basically, pad the second coll with an infinite sequence of nils. map stops when it reaches the end of the first collection.
An (eager) loop/recur form that walks to end of longest:
(loop [c1 [0 1 2] c2 [:0 :1] o []]
(if (or (seq c1) (seq c2))
(recur (rest c1) (rest c2) (conj o (f (first c1) (first c2))))
o))
Or you could write a lazy version of map that did something similar.
A general lazy version, as suggested by Alex Miller's answer, is
(defn map-all [f & colls]
(lazy-seq
(when-not (not-any? seq colls)
(cons
(apply f (map first colls))
(apply map-all f (map rest colls))))))
For example,
(map-all vector [0 1 2] [:0 :1])
;([0 :0] [1 :1] [2 nil])
You would probably want to specialise map-all for one and two collections.
just for fun
this could easily be done with common lisp's do macro. We could implement it in clojure and do this (and much more fun things) with it:
(defmacro cl-do [clauses [end-check result] & body]
(let [clauses (map #(if (coll? %) % (list %)) clauses)
bindings (mapcat (juxt first second) clauses)
nexts (map #(nth % 2 (first %)) clauses)]
`(loop [~#bindings]
(if ~end-check
~result
(do
~#body
(recur ~#nexts))))))
and then just use it for mapping (notice it can operate on more than 2 colls):
(defn map-all [f & colls]
(cl-do ((colls colls (map next colls))
(res [] (conj res (apply f (map first colls)))))
((every? empty? colls) res)))
in repl:
user> (map-all vector [1 2 3] [:a :s] '[z x c v])
;;=> [[1 :a z] [2 :s x] [3 nil c] [nil nil v]]

4clojure: set-intersection, recursive lambda

I am attempting to write a recursive lambda function (accepting two arguments) that finds the intersection between two (potentially) unsorted sets. Here is the code, which I believe most will find straight forward:
(fn intersection [a b]
(fn inner [x y out]
(if (empty? x) out
(if (nil? (some ((set (first x)) y )))
(inner (rest x) y out))))
(inner (rest x) y (cons (first x) out))
(inner a b '[])
)
I hope to use this lambda function intersection in place of the underscore _ that follows:
(= (__ #{0 1 2 3} #{2 3 4 5}) #{2 3})
However, this code fails to compile, insisting that Java is Unable to resolve symbol: inner in this context...
Any suggestions?
source: http://www.4clojure.com/problem/81
You could try :
#(set (filter %1 %2))
Since sets are functions (see another useful example there). The syntax with %1 and %2 is the same as writing :
(fn [s1 s2] (set (filter s1 s2)))
Or even more succinctly :
(comp set filter)
Regarding the inner error, you simply misplaced the parens (but I don't really see the logic otherwise) :
(fn intersection [a b]
(fn inner [x y out]
(if (empty? x) out
(if (nil? (some ((set (first x)) y )))
(inner (rest x) y out)))
(inner (rest x) y (cons (first x) out))
(inner a b '[])))
If you insist on building the set manually (which I did not so long ago), you could end up with something like that :
(fn [s1 s2]
(reduce #(if (contains? s2 %2) (conj %1 %2) %1) #{} s1))
But really the first solution is the most elegant.

Does clojure have the C# equivalent of yield?

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

Grouping words and more

I'm working on a project to learn Clojure in practice. I'm doing well, but sometimes I get stuck. This time I need to transform sequence of the form:
[":keyword0" "word0" "word1" ":keyword1" "word2" "word3"]
into:
[[:keyword0 "word0" "word1"] [:keyword1 "word2" "word3"]]
I'm trying for at least two hours, but I know not so many Clojure functions to compose something useful to solve the problem in functional manner.
I think that this transformation should include some partition, here is my attempt:
(partition-by (fn [x] (.startsWith x ":")) *1)
But the result looks like this:
((":keyword0") ("word1" "word2") (":keyword1") ("word3" "word4"))
Now I should group it again... I doubt that I'm doing right things here... Also, I need to convert strings (only those that begin with :) into keywords. I think this combination should work:
(keyword (subs ":keyword0" 1))
How to write a function which performs the transformation in most idiomatic way?
Here is a high performance version, using reduce
(reduce (fn [acc next]
(if (.startsWith next ":")
(conj acc [(-> next (subs 1) keyword)])
(conj (pop acc) (conj (peek acc)
next))))
[] data)
Alternatively, you could extend your code like this
(->> data
(partition-by #(.startsWith % ":"))
(partition 2)
(map (fn [[[kw-str] strs]]
(cons (-> kw-str
(subs 1)
keyword)
strs))))
what about that:
(defn group-that [ arg ]
(if (not-empty arg)
(loop [list arg, acc [], result []]
(if (not-empty list)
(if (.startsWith (first list) ":")
(if (not-empty acc)
(recur (rest list) (vector (first list)) (conj result acc))
(recur (rest list) (vector (first list)) result))
(recur (rest list) (conj acc (first list)) result))
(conj result acc)
))))
Just 1x iteration over the Seq and without any need of macros.
Since the question is already here... This is my best effort:
(def data [":keyword0" "word0" "word1" ":keyword1" "word2" "word3"])
(->> data
(partition-by (fn [x] (.startsWith x ":")))
(partition 2)
(map (fn [[[k] w]] (apply conj [(keyword (subs k 1))] w))))
I'm still looking for a better solution or criticism of this one.
First, let's construct a function that breaks vector v into sub-vectors, the breaks occurring everywhere property pred holds.
(defn breakv-by [pred v]
(let [break-points (filter identity (map-indexed (fn [n x] (when (pred x) n)) v))
starts (cons 0 break-points)
finishes (concat break-points [(count v)])]
(mapv (partial subvec v) starts finishes)))
For our case, given
(def data [":keyword0" "word0" "word1" ":keyword1" "word2" "word3"])
then
(breakv-by #(= (first %) \:) data)
produces
[[] [":keyword0" "word0" "word1"] [":keyword1" "word2" "word3"]]
Notice that the initial sub-vector is different:
It has no element for which the predicate holds.
It can be of length zero.
All the others
start with their only element for which the predicate holds and
are at least of length 1.
So breakv-by behaves properly with data that
doesn't start with a breaking element or
has a succession of breaking elements.
For the purposes of the question, we need to muck about with what breakv-by produces somewhat:
(let [pieces (breakv-by #(= (first %) \:) data)]
(mapv
#(update-in % [0] (fn [s] (keyword (subs s 1))))
(rest pieces)))
;[[:keyword0 "word0" "word1"] [:keyword1 "word2" "word3"]]