I am new to Clojure and I'm learning how to write a program that can simplify logical expressions (just 'and' for now to figure out how things work first). For example:
(and-simplify '(and true)) => true
(and-simplify '(and x true)) => x
(and-simplify '(and true false x)) => false
(and-simplify '(and x y z true)) => (and x y z)
I already knew how to simplify two arguments, that everything I can do right now is:
(defn and-simplify []
(def x (and true false))
println x)
(and-simplify)
I've read this post and tried to modify my code a little bit but it doesn't seem to get me anywhere:
(defn and-simplify [&expr]
(def (and &expr))
)
What is the correct way that I should have done?
Here's my take on it.
(defn simplify-and
[[op & forms]]
(let [known-falsy? #(or (false? %) (nil? %))
known-truthy? #(and (not (symbol? %))
(not (seq? %))
(not (known-falsy? %)))
falsy-forms (filter known-falsy? forms)
unknown-forms (remove known-truthy? forms)]
(if (seq falsy-forms)
(first falsy-forms)
(case (count unknown-forms)
0 true
1 (first unknown-forms)
(cons op unknown-forms)))))
(comment (simplify-and `(and true 1 2 a)))
However, we can write a more generic simplify that uses multimethods to simplify lists, so that we can add more optimisations without modifying existing code. Here's that, with optimisations for and, or and + from clojure.core. This simplify only optimises lists based on namespace qualified names.
Check out the examples in the comment form. Hope it makes sense.
(defn- known-falsy? [form]
(or (false? form) (nil? form)))
(defn- known-truthy? [form]
(and (not (symbol? form))
(not (seq? form))
(not (known-falsy? form))))
(declare simplify)
(defmulti simplify-list first)
(defmethod simplify-list :default [form] form)
(defmethod simplify-list 'clojure.core/and
[[op & forms]]
(let [forms (mapv simplify forms)
falsy-forms (filter known-falsy? forms)
unknown-forms (remove known-truthy? forms)]
(if (seq falsy-forms)
(first falsy-forms)
(case (count unknown-forms)
0 true
1 (first unknown-forms)
(cons op unknown-forms)))))
(defmethod simplify-list 'clojure.core/or
[[op & forms]]
(let [forms (mapv simplify forms)
truthy-forms (filter known-truthy? forms)
unknown-forms (remove known-falsy? forms)]
(if (seq truthy-forms)
(first truthy-forms)
(case (count unknown-forms)
0 nil
1 (first unknown-forms)
(cons op unknown-forms)))))
(defmethod simplify-list 'clojure.core/+
[[op & forms]]
(let [{nums true non-nums false} (group-by number? (mapv simplify forms))
sum (apply + nums)]
(if (seq non-nums)
(cons op (cons sum non-nums))
sum)))
(defn simplify
"takes a Clojure form with resolved symbols and performs
peephole optimisations on it"
[form]
(cond (set? form) (into #{} (map simplify) form)
(vector? form) (mapv simplify form)
(map? form) (reduce-kv (fn [m k v] (assoc m (simplify k) (simplify v)))
{} form)
(seq? form) (simplify-list form)
:else form))
(comment
(simplify `(+ 1 2))
(simplify `(foo 1 2))
(simplify `(and true (+ 1 2 3 4 5 foo)))
(simplify `(or false x))
(simplify `(or false x nil y))
(simplify `(or false x (and y nil z) (+ 1 2)))
)
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")
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))
Say there is the need to check if an argument passes one truth test of a given predicate collection.
codewise:
(fn [x]
(or (pred1 x) (pred2 x) (pred3 x) (pred4 x)))
due to the implementation of or, this short circuits after the first truthy value. As intended.
How can this be rewritten by using a collection of predicates:
[pred1 pred2 pred3 pred4]
A funky way would be:
(fn [x preds]
(some? ;; nil->false
(some true? (map #(% x) preds))))
It also turns out that this one does not short circuit. Might be due to Clojure's chunking of lazy sequences.
Can we do this better?
clojure has a some-fn function for that:
user> ((some-fn true? false? nil?) true)
true
user> ((some-fn false? nil?) true)
false
or for your case:
user> (defn any-pred? [x preds]
((apply some-fn preds) x))
another classic way is to do it recursively:
user> (defn any-pred? [x preds]
(when-let [[pred & preds] (seq preds)]
(or (pred x) (any-pred? x preds))))
user> (any-pred? true [false?])
nil
user> (any-pred? true [true?])
true
user> (any-pred? true [false? true?])
true
user> (any-pred? true [false? nil?])
nil
user> (any-pred? true [false? nil? true?])
true
I think it's map that's doing the chunking in your solution.
Try
(defn any-true? [preds]
(fn [x]
(loop [preds preds]
(and (seq preds)
(or ((first preds) x)
(recur (rest preds)))))))
((any-true? [odd? even?]) 3) ;true
((any-true? []) 3) ;nil
((any-true? [even?]) 3) ;nil
((any-true? [odd? #(/ % 0)]) 3) ;true
The last example shows that the evaluation is lazy.
When I need short circuit, I use reduce with reduced.
(defn any-valid? [w & pred-fn-coll]
(reduce (fn [v pf]
(if (pf w)
(reduced true)
v)) false pred-fn-coll))
(any-valid? 1 even? odd?)
;=> true
(any-valid? 1 even? even?)
;=> false
Alternatively,
(defn somep? [x [p & ps :as preds]]
(if-not (empty? preds)
(or (p x) (somep? x ps))))
or
(defn somep? [x [p & ps :as preds]]
(if-not (empty? preds)
(let [res (p x)]
(if-not res
(recur x ps)
res))))
I have the following functions that check for odd parity in sequence
(defn countOf[a-seq elem]
(loop [number 0 currentSeq a-seq]
(cond (empty? currentSeq) number
(= (first currentSeq) elem) (recur (inc number) (rest currentSeq))
:else (recur number (rest currentSeq))
)
)
)
(defn filteredSeq[a-seq elemToRemove]
(remove (set (vector (first a-seq))) a-seq)
)
(defn parity [a-seq]
(loop [resultset [] currentSeq a-seq]
(cond (empty? currentSeq) (set resultset)
(odd? (countOf currentSeq (first currentSeq))) (recur (concat resultset (vector(first currentSeq))) (filteredSeq currentSeq (first currentSeq)))
:else (recur resultset (filteredSeq currentSeq (first currentSeq)))
)
)
)
for example (parity [1 1 1 2 2 3]) -> (1 3) that is it picks odd number of elements from a sequence.
Is there a better way to achieve this?
How can this be done with reduce function of clojure
First, I decided to make more idiomatic versions of your code, so I could really see what it was doing:
;; idiomatic naming
;; no need to rewrite count and filter for this code
;; putting item and collection in idiomatic argument order
(defn count-of [elem a-seq]
(count (filter #(= elem %) a-seq)))
;; idiomatic naming
;; putting item and collection in idiomatic argument order
;; actually used the elem-to-remove argument
(defn filtered-seq [elem-to-remove a-seq]
(remove #(= elem-to-remove %) a-seq))
;; idiomatic naming
;; if you want a set, use a set from the beginning
;; destructuring rather than repeated usage of first
;; use rest to recur when the first item is guaranteed to be dropped
(defn idiomatic-parity [a-seq]
(loop [result-set #{}
[elem & others :as current-seq] a-seq]
(cond (empty? current-seq)
result-set
(odd? (count-of elem current-seq))
(recur (conj result-set elem) (filtered-seq elem others))
:else
(recur result-set (filtered-seq elem others)))))
Next, as requested, a version that uses reduce to accumulate the result:
;; mapcat allows us to return 0 or more results for each input
(defn reducing-parity [a-seq]
(set
(mapcat
(fn [[k v]]
(when (odd? v) [k]))
(reduce (fn [result item]
(update-in result [item] (fnil inc 0)))
{}
a-seq))))
But, reading over this, I notice that the reduce is just frequencies, a built in clojure function. And my mapcat was really just a hand-rolled keep, another built in.
(defn most-idiomatic-parity [a-seq]
(set
(keep
(fn [[k v]]
(when (odd? v) k))
(frequencies a-seq))))
In Clojure we can refine our code, and as we recognize places where our logic replicates the built in functionality, we can simplify the code and make it more clear. Also, there is a good chance the built in is better optimized than our own work-alikes.
Is there a better way to achieve this?
(defn parity [coll]
(->> coll
frequencies
(filter (fn [[_ v]] (odd? v)))
(map first)
set))
For example,
(parity [1 1 1 2 1 2 1 3])
;#{1 3}
How can this be done with reduce function of clojure.
We can use reduce to rewrite frequencies:
(defn frequencies [coll]
(reduce
(fn [acc x] (assoc acc x (inc (get acc x 0))))
{}
coll))
... and again to implement parity in terms of it:
(defn parity [coll]
(let [freqs (frequencies coll)]
(reduce (fn [s [k v]] (if (odd? v) (conj s k) s)) #{} freqs)))
As a neophyte clojurian, it was recommended to me that I go through the Project Euler problems as a way to learn the language. Its definitely a great way to improve your skills and gain confidence. I just finished up my answer to problem #14. It works fine, but to get it running efficiently I had to implement some memoization. I couldn't use the prepackaged memoize function because of the way my code was structured, and I think it was a good experience to roll my own anyways. My question is if there is a good way to encapsulate my cache within the function itself, or if I have to define an external cache like I have done. Also, any tips to make my code more idiomatic would be appreciated.
(use 'clojure.test)
(def mem (atom {}))
(with-test
(defn chain-length
([x] (chain-length x x 0))
([start-val x c]
(if-let [e (last(find #mem x))]
(let [ret (+ c e)]
(swap! mem assoc start-val ret)
ret)
(if (<= x 1)
(let [ret (+ c 1)]
(swap! mem assoc start-val ret)
ret)
(if (even? x)
(recur start-val (/ x 2) (+ c 1))
(recur start-val (+ 1 (* x 3)) (+ c 1)))))))
(is (= 10 (chain-length 13))))
(with-test
(defn longest-chain
([] (longest-chain 2 0 0))
([c max start-num]
(if (>= c 1000000)
start-num
(let [l (chain-length c)]
(if (> l max)
(recur (+ 1 c) l c)
(recur (+ 1 c) max start-num))))))
(is (= 837799 (longest-chain))))
Since you want the cache to be shared between all invocations of chain-length, you would write chain-length as (let [mem (atom {})] (defn chain-length ...)) so that it would only be visible to chain-length.
In this case, since the longest chain is sufficiently small, you could define chain-length using the naive recursive method and use Clojure's builtin memoize function on that.
Here's an idiomatic(?) version using plain old memoize.
(def chain-length
(memoize
(fn [n]
(cond
(== n 1) 1
(even? n) (inc (chain-length (/ n 2)))
:else (inc (chain-length (inc (* 3 n))))))))
(defn longest-chain [start end]
(reduce (fn [x y]
(if (> (second x) (second y)) x y))
(for [n (range start (inc end))]
[n (chain-length n)])))
If you have an urge to use recur, consider map or reduce first. They often do what you want, and sometimes do it better/faster, since they take advantage of chunked seqs.
(inc x) is like (+ 1 x), but inc is about twice as fast.
You can capture the surrounding environment in a clojure :
(defn my-memoize [f]
(let [cache (atom {})]
(fn [x]
(let [cy (get #cache x)]
(if (nil? cy)
(let [fx (f x)]
(reset! cache (assoc #cache x fx)) fx) cy)))))
(defn mul2 [x] (do (print "Hello") (* 2 x)))
(def mmul2 (my-memoize mul2))
user=> (mmul2 2)
Hello4
user=> (mmul2 2)
4
You see the mul2 funciton is only called once.
So the 'cache' is captured by the clojure and can be used to store the values.