Clojure cond or if? - clojure

Currently I am programming a map that will take an amount of money from one id and transfer it to another but whenever I add an if or cond statement to make sure that they have enough in their balance I keep getting a sending too many arguments error. if I use cond It says that cond requires an even number of forms. Any help would be appreciated
Code:
(defn transfer[ledger from-id to-id amt]
(dosync
(let [from-id (get #ledger from-id)]
(let [to-id (get #ledger to-id) ]
(if from-id
(if to-id
(let [bal1 (get from-id :balance)]
(let [bal2 (get to-id :balance)]
(cond (< amt bal1)
(alter bal1 - amt)
(alter bal2 + amt)
:else "Too much"
)
nil
nil
))))))))

It's not about cond or if, but it's on "how to group multiple s-exps into one and evaluate them sequentially".
Use do as in:
(defn transfer[ledger from-id to-id amt]
(dosync
(let [from-id (get #ledger from-id)
to-id (get #ledger to-id)]
(if (and from-id to-id)
(let [bal1 (:balance from-id)
bal2 (:balance to-id)]
(if (< amt bal1)
(do
(alter bal1 - amt)
(alter bal2 + amt))
"Too much"))))))

Related

Clojure Dashboard query

I am trying to show a graph on riemann-dashboard using query
"pingDelay > 0" .
I already have indexed my data using following code
(let [index (index)]
(defn write-dht-metric [e]
(let [dhtstate (re-find #"dht_status: health\.(\S+), msg count (\d+) \((\d+) bytes\).*peak \{ping = (\d+)" (:pgmsg e))]
(if (not= dhtstate nil)
(do
(prn "RESULT>" dhtstate)
(index {:host "dht-info"
:service (:service e)
:time (unix-time)
:dhtStatus (get dhtstate 1)
:msgCount (get dhtstate 2)
:pingDelay (get dhtstate 3)}
)
)
)
)
)
)
However, I am not getting anything on graph. Earlier, I thought that perhaps its because my "pingDelay" is in string "12345", so, i also tried ":pingDelay #(Long. (get dhtstate 3))" without any success.
Can anyone please help me about what I must do to make it work?
Regards
defining top level forms in function calls is a little odd. It works only because defining a var returns that var to the calling form. It's more typical to write it like:
(defn write-dht-metric [e]
(let [dhtstate (re-find #"dht_status: health\.(\S+), msg count (\d+) \((\d+) bytes\).*peak \{ping = (\d+)" (:pgmsg e))]
(if (not= dhtstate nil)
(do
(prn "RESULT>" dhtstate)
(index {:host "dht-info"
:service (:service e)
:time (unix-time)
:dhtStatus (get dhtstate 1)
:msgCount (get dhtstate 2)
:pingDelay (get dhtstate 3)})))))
(let [index (index)]
(streams
write-dht-metric))
there are several other ways of writing it:
(defn write-dht-metric [e]
(let [dhstate (:dhstate e)]
(prn "RESULT>" dhtstate)
(index {:host "dht-info"
:service (:service e)
:time (unix-time)
:dhtStatus (get dhtstate 1)
:msgCount (get dhtstate 2)
:pingDelay (get dhtstate 3)})))
(let [index (index)]
(streams
(with :dhstate (re-find #"dht_status: health\.(\S+), msg count (\d+) \((\d+) bytes\).*peak \{ping = (\d+)" (:pgmsg event))
(when :dhstate
write-dht-metric)))
It turned out that I had to write value of my pingDelay in ":metric field". It starts working now.

Efficiently create and diff sets created from large text file

I am attempting to copy about 12 million documents in an AWS S3 bucket to give them new names. The names previously had a prefix and will now all be document name only. So a/b/123 once renamed will be 123. The last segment is a uuid so there will not be any naming collisions.
This process has been partially completed so some have been copied and some still need to be. I have a text file that contains all of the document names. I would like an efficient way to determine which documents have not yet been moved.
I have some naive code that shows what I would like to accomplish.
(def doc-names ["o/123" "o/234" "t/543" "t/678" "123" "234" "678"])
(defn still-need-copied [doc-names]
(let [last-segment (fn [doc-name]
(last (clojure.string/split doc-name #"/")))
by-position (group-by #(.contains % "/") doc-names)
top (set (get by-position false))
nested (set (map #(last-segment %) (get by-position true)))
needs-copied (clojure.set/difference nested top)]
(filter #(contains? needs-copied (last-segment %)) doc-names)))
I would propose this solution:
(defn still-need-copied [doc-names]
(->> doc-names
(group-by #(last (clojure.string/split % #"/")))
(keep #(when (== 1 (count (val %))) (first (val %))))))
first you group all the items by the last element split string, getting this for your input:
{"123" ["o/123" "123"],
"234" ["o/234" "234"],
"543" ["t/543"],
"678" ["t/678" "678"]}
and then you just need to select all the values of a map, having length of 1, and to take their first elements.
I would say it is way more readable than your variant, and also seems to be more productive.
That's why:
as far as I can understand, your code here probably has a complexity of
N (grouping to a map with just 2 keys) +
Nlog(N) (creation and filling of top set) +
Nlog(N) (creation and filling of nested set) +
Nlog(N) (sets difference) +
Nlog(N) (filtering + searching each element in a needs-copied set) =
4Nlog(N) + N
whereas my variant would probably have the complexity of
Nlog(N) (grouping values into a map with a large amount of keys) +
N (keeping needed values) =
N + Nlog(N)
And though asymptotically they are both O(Nlog(N)), practically mine will probably complete faster.
ps: Not an expert in the complexity theory. Just made some very rough estimation
here is a little test:
(defn generate-data [len]
(doall (mapcat
#(let [n (rand-int 2)]
(if (zero? n)
[(str "aaa/" %) (str %)]
[(str %)]))
(range len))))
(defn still-need-copied [doc-names]
(let [last-segment (fn [doc-name]
(last (clojure.string/split doc-name #"/")))
by-position (group-by #(.contains % "/") doc-names)
top (set (get by-position false))
nested (set (map #(last-segment %) (get by-position true)))
needs-copied (clojure.set/difference nested top)]
(filter #(contains? needs-copied (last-segment %)) doc-names)))
(defn still-need-copied-2 [doc-names]
(->> doc-names
(group-by #(last (clojure.string/split % #"/")))
(keep #(when (== 1 (count (val %))) (first (val %))))))
(def data-100k (generate-data 100000))
(def data-1m (generate-data 1000000))
user> (let [_ (time (dorun (still-need-copied data-100k)))
_ (time (dorun (still-need-copied-2 data-100k)))
_ (time (dorun (still-need-copied data-1m)))
_ (time (dorun (still-need-copied-2 data-1m)))])
"Elapsed time: 714.929641 msecs"
"Elapsed time: 243.918466 msecs"
"Elapsed time: 7094.333425 msecs"
"Elapsed time: 2329.75247 msecs"
so it is ~3 times faster, just as I predicted
update:
found one solution, which is not so elegant, but seems to be working.
You said you're using iota, so i've generated a huge file with the lines of ~15 millions of lines (with forementioned generate-data fn)
then i've decided to sort if by the last part after slash (so that "123" and "aaa/123" stand together.
(defn last-part [s] (last (clojure.string/split s #"/")))
(def sorted (sort-by last-part (iota/seq "my/file/path")))
it has completed surprisingly fast. So the last thing i had to do, is to make a simple loop checking for every item if there is an item with the same last part nearby:
(def res (loop [res [] [item1 & [item2 & rest :as tail] :as coll] sorted]
(cond (empty? coll) res
(empty? tail) (conj res item1)
(= (last-part item1) (last-part item2)) (recur res rest)
:else (recur (conj res item1) tail))))
it has also completed without any visible difficulties, so i've got the needed result without any map/reduce framework.
I think also, that if you won't keep the sorted coll in a var, you would probably save memory by avoiding the huge coll head retention:
(def res (loop [res []
[item1 & [item2 & rest :as tail] :as coll] (sort-by last-part (iota/seq "my/file/path"))]
(cond (empty? coll) res
(empty? tail) (conj res item1)
(= (last-part item1) (last-part item2)) (recur res rest)
:else (recur (conj res item1) tail))))

How could I write a function call once with an nested let in an if-let?

I have these functions:
(def i (atom {})) ;incremented/calculated file stats
(defn updatei [n fic fos]
(swap! i conj {(keyword n)[fic fos]}))
(defn calcfo [fo fi fis]
(if-let [n (get #i fo)] ;find a previous record?
(let [fic (inc (first n)), fos (+ fis (second n))] ;increment the stats
(updatei fo fic fos))
(let [fic 1, fos fis] ;if not then: first calc recorded
(updatei fo fic fos))))
How could I write (updatei fo fic fos) once, instead of having it listed twice in the function? Is there a secret or-let I am unaware of?
-Hypothetical code-
(defn calcfo [fo fi fis]
(if-let [n (get #i fo)] ;find a previous record?
(let [fic (inc (first n)), fos (+ fis (second n))] ;increment the stats
(or-let [fic 1, fos fis] ;if not then: first calc recorded
(updatei fo fic fos)))))
Or am I thinking of this too imperatively versus functionally?
EDIT:
I decided this made the most sense to me:
(defn calcfo [fo fis]
(apply updatei fo
(if-let [[rfc rfos] (get #i fo)] ;find a previous record?
[(inc rfc) (+ rfos fis)] ;increment the stats
[1 fis]) ;if not then: first calc recorded
))
Thanks for the great answers!
A rearrangement might help
(defn calcfo [fo fi fis]
(apply updatei fo
(if-let [n (get #i fo)]
[(inc (first n)), (+ fis (second n))]
[1, fis] )))
What about using an if and then destructuring? Here's an approach:
(defn calcfo [fo fi fis]
(let [n (get #i fo) ;find a previous record?
[fic fos] (if n
[(-> n first inc) (-> n second (+ fis))] ;increment the stats
[1 fis])] ;if not then: first calc recorded
(updatei fo fic fos)))
The argument fi doesn't seem to be being used so maybe you could remove it from the argument list.
(defn calcfo [fo fis] ,,,)
The usage of first and second could also be avoided with the use of destructuring when binding n in the let form:
(defn calcfo [fo fis]
(let [[x y & _] (get #i fo)
[fic fos] (if x [(inc x) (+ fis y)] [1 fis])]
(updatei fo fic fos)))
I think you would sidestep the whole problem and make your code better if you rewrote updatei, something like:
(defn- safe+ [a b]
(if a (if b (+ a b) a) b))
(defn updatei [n fic fos]
(swap! i update-in [(keyword n)] #(vector (safe+ fic (first %)) (safe+ fos (second %)))))
There may be a better way to write that code, but the basic idea is to use update-in to either store the new values (if nothing was stored for that key before), or combine them with what is already there.

How do I rewrite (def) out of this Clojure code?

I have written a game loop based on deWitter's game loop.
However, I am unsure how to transfer it to a more functional state. I realize that there may need to be some mutable state left within the code but are there any general principles for cleaning up extraneous defs?
(ns beepboop.core)
(def ticks-per-second 25)
(def skip-ticks (/ 1000 ticks-per-second))
(def max-frameskip 5)
(defn update []
(println "Updating."))
(defn display [delta]
(println "Displaying with delta: " delta))
(defn -main []
(def *next-tick* (System/currentTimeMillis))
(while true
(def *loops* 0)
(while (and
(> (System/currentTimeMillis)
*next-tick*)
(< *loops*
max-frameskip))
(update)
(def *next-tick* (+ *next-tick* skip-ticks))
(def *loops* (+ *loops* 1)))
(display
(/ (+ (System/currentTimeMillis) skip-ticks (* -1 *next-tick*))
skip-ticks))))
You should use loop and recur for updating your loop variables:
(defn -main []
(loop [next-tick (System/currentTimeMillis)]
(let [next-next
(loop [next-tick next-tick
loops 0]
(if (and (> (System/currentTimeMillis) next-tick)
(< loops max-frameskip))
(do (update)
(recur (+ next-tick skip-ticks) (+ loops 1)))
next-tick))]
(display (/ (+ (System/currentTimeMillis) skip-ticks (- next-next))
skip-ticks))
(recur next-next))))

Threadsafe pop in clojure?

I've found this code on http://www.learningclojure.com/2010/11/yet-another-way-to-write-factorial.html, but I don't understand if/how the pop-task is supposed to be threadsafe. Doesn't it allow to return twice the same head ?
(def to-do-list (atom '()))
(defn add-task![t] (swap! to-do-list #(cons t %)))
(defn pop-task![] (let [h (first #to-do-list)] (swap! to-do-list rest) h))
If so, is it possible to keep using atom and write the peek and swap! atomically, or is this a job for the ref mechanism ?
Or you drop to a lower level.
(def to-do-list (atom nil))
(defn add-task!
[t]
(swap! to-do-list conj t))
(defn pop-task!
[]
(let [[h & r :as l] #to-do-list]
(if (compare-and-set! to-do-list l r)
h
(recur))))
Yeah, that code isn't thread safe. You can make it thread-safe by taking advantage of the fact that swap! returns the new value of the atom, which implies you need to combine the queue with the "popped" value.
(def to-do-list
(atom {}))
(defn add-task!
[t]
(swap! to-do-list
(fn [tl]
{:queue (cons t (:queue tl))})))
(defn pop-task!
[]
(let [tl (swap! to-do-list
(fn [old]
{:val (first (:queue old))
:queue (rest (:queue old))}))]
(:val tl)))