For example
(def inc-map (let [inum (atom 0)]
{:countup (fn[](swap! inum inc))
:get (fn[](#inum))}))
((inc-map :countup )) ;increase inside value
; ⇒ 1
((inc-map :get)) ;get current value
; ⇒ 1
Can I get access inum when I want to add more functions later?
E.g. I want to do this:
(def inc-map
(assoc inc-map :countdown (fn[] ???)))
How can I access inum at ????
it could be possible, if you expose one more function, say :update, enclosing the inum value.
(def inc-map (let [inum (atom 0)]
{:update (fn [f & args] (apply swap! inum f args))
:countup (fn [] (swap! inum inc))
:get (fn [] #inum)}))
user> (def inc-dec-map (assoc inc-map :countdown
(fn [] ((inc-map :update) dec))))
#'user/inc-dec-map
user> ((inc-dec-map :countup))
;;=> 1
user> ((inc-dec-map :countup))
;;=> 2
user> ((inc-dec-map :countdown))
;;=> 1
user> ((inc-dec-map :countdown))
;;=> 0
user> ((inc-dec-map :countdown))
;;=> -1
and then you can just seal it, dissoc'ing :update, (say if you make it publicly accessed to some other namespace.
otherwise you could provide an getter/updater to the ops object:
(def counter-ops (let [inum (atom 0)
ops (atom {:countup (fn [] (swap! inum inc))
:get (fn [] #inum)})]
(fn
;; get operations map snapshot
([] #ops)
;; get operation
([op] (-> ops deref op))
;; set operation
([op f & args] (swap! ops assoc op (fn [& args] (apply f inum args)))))))
user> (counter-ops :countdown (fn [inum] (swap! inum dec)))
user> (counter-ops :decrease-by (fn [inum n] (swap! inum - n)))
user> ((counter-ops :countdown))
;;=> -1
user> ((counter-ops :countdown))
;;=> -2
user> ((counter-ops :countup))
;;=> -1
user> ((counter-ops :countup))
;;=> 0
user> ((counter-ops :decrease-by) 10)
;;=> -10
seal it to be locked for any subsequent op additions:
user> (def counter-ops-sealed (counter-ops))
#'user/counter-ops-sealed
user> ((counter-ops-sealed :countup))
;;=> 2
user> ((counter-ops-sealed :countdown))
;;=> 1
Related
Is there a short form/macro that allows me to do
(defn f [a b c]
{a b c})
instead of
(defn f [a b c]
{:a a :b b :c c})
(defmacro as-map [& syms]
(zipmap (map keyword syms) syms))
Usage:
(def a 42)
(def b :foo)
(as-map a b)
;;-> {:a 42 :b :foo}
Note that to support namespaced keywords, you'd have to drop support for ns aliases if you want to keep it as short:
(defmacro as-map [& syms]
(zipmap (map keyword syms) (map (comp symbol name) syms)))
Usage:
(def a 42)
(def b :foo)
(as-map example/a foo-of/b)
;;-> {:example/a 42 :foo-of/b :foo}
Advice: Likely not a good idea, saves you a few keyboard hits at the cost of readability and expressivity and flexibility in naming local bindings.
This shows the steps. Remove the println's for actual use:
(ns clj.core
(:gen-class))
(defmacro hasher [& args]
(let [keywords (map keyword args)
values args
keyvals-list (interleave keywords values)
]
(println "keywords " keywords)
(println "values " values)
(println "keyvals-list " keyvals-list)
`(hash-map ~#keyvals-list)
)
)
(def a 1)
(def b 2)
(println \newline "result: " (hasher a b))
> lein run
keywords (:a :b)
values (a b)
keyvals-list (:a a :b b)
result: {:b 2, :a 1}
This is an old snippet of mine I've had kicking around for a while.
(declare ^:private restructure*)
(defn ^:private restructure-1 [m [e k]]
(cond
(= :strs e) (reduce #(assoc %1 (name %2) %2) m k)
(= :keys e) (reduce #(assoc %1 (keyword (namespace %2) (name %2)) %2) m k)
:else (assoc m k (restructure* e))))
(defn ^:private restructure* [form]
(if-not (map? form)
form
(as-> {} v
(reduce restructure-1 v form)
`(hash-map ~#(mapcat identity v)))))
(defmacro restructure [form]
(restructure* form))
The idea is that it provides the complement of clojure.core/destructure which goes from a destructuring form to bindings, this captures bindings and constructs a datastructure.
(let [x 1 y 2 z 3]
(restructure {:keys [x y z]}))
;; => {:x 1 :y 2 :z 3}
Here is the code:
(def city-nodes (atom nil))
(def city-edges (atom nil))
(def player-pos (ref nil))
(def visited-nodes (ref #{}))
(def player-status (atom :in-progress))
(def ^:dynamic *node-num* 8)
(def ^:dynamic *edge-num* 5)
(def ^:dynamic *worm-num* 3)
(def ^:dynamic *cop-odds* 4)
(defn filterb
[pred coll]
(let [rt (filter pred coll)]
(if (empty? rt)
nil
rt)))
(defn all-nodes []
(range 1 (inc *node-num*)))
(all-nodes)
(defn rand-node []
(inc (rand-int *node-num*)))
(defn edge-pair [x y]
(when-not (= x y)
[[x y] [y x]]))
(defn sample-2diff-nodes []
(let [[x y] [(rand-node) (rand-node)]]
(if (= x y)
(sample-2diff-nodes)
[x y])))
(edge-pair 1 1)
(take 4 (repeatedly #(sample-2diff-nodes)))
(def ^:dynamic t-edges (take 4 (repeatedly #(sample-2diff-nodes))))
(defn make-edge-vec []
(->> (repeatedly #(sample-2diff-nodes))
(take *edge-num*)
(apply concat)
set
vec))
(binding [t-edges (take 4 (repeatedly #(sample-2diff-nodes)))])
(prn t-edges)
(binding [t-edges (make-edge-vec)])
(prn t-edges)
(binding [t-edges (apply concat t-edges)])
(prn t-edges)
Results:
(binding [t-edges (take 4 (repeatedly #(sample-2diff-nodes)))])
(prn t-edges)
(binding [t-edges (make-edge-vec)])
(prn t-edges)
(binding [t-edges (apply concat t-edges)])
(prn t-edges)
([6 1] [1 2] [5 2] [3 1])
([6 1] [1 2] [5 2] [3 1])
([6 1] [1 2] [5 2] [3 1])
nil
I for the last prn command, I expected ( 6 1 1 2 5 2 3 1).
the closing ) of the binding call is in the wrong place:
(binding [t-edges (apply concat t-edges)])
(prn t-edges)
should be:
(binding [t-edges (apply concat t-edges)]
(prn t-edges))
Though in this case you almost certainly want to use a let expression instead
I have the following code, defining a type that has an atom in there.
(defprotocol IDeck
(vec-* [dk] "Output to a persistent vector")
(count-* [dk] "Number of elements in the deck")
(conj1-* [dk & es] "Adding multiple elements to the deck"))
(deftype ADeck [#^clojure.lang.Atom val]
IDeck
(vec-* [dk] (->> (.val dk) deref (map deref) vec))
(count-* [dk] (-> (.val dk) deref count))
(conj1-* [dk & es]
(try
(loop [esi es]
(let [e (first esi)]
(cond
(nil? e) dk
:else
(do
(swap! (.val dk) #(conj % (atom e)))
(recur (rest esi))))))
(catch Throwable t (println t)))))
(defn new-*adeck
([] (ADeck. (atom [])))
([v] (ADeck. (atom (vec (map atom v))))))
(defn conj2-* [dk & es]
(try
(loop [esi es]
(let [e (first esi)]
(cond
(nil? e) dk
:else
(do
(swap! (.val dk) #(conj % (atom e)))
(recur (rest esi))))))
(catch Throwable t (println t))))
;; Usage
(def a (new-*adeck [1 2 3 4]))
(count-* a)
;=> 4
(vec-* a)
;=> [1 2 3 4]
(conj1-* a 1 2) ;; The deftype case
;=> IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
(vec-* a)
;=> [1 2 3 4]
(conj2-* a 1 2) ;; The defn case
(vec-* a)
;=> [1 2 3 4 1 2]
Even though the two conj-* methods are exactly the same, except that one is in a deftype and the other is a normal defn, the first gives an error while the second succeeds. Why is this?
This is because protocols doesn't support variable number of arguments.
What you can do is make:
(conj1-* [dk & es] "Adding multiple elements to the deck"))
into
(conj1-* [dk es] "Adding multiple elements to the deck"))
such that the es param will be vector and called like:
(conj1-* a [1 2])
The following code appears to force line-seq to read 4 lines from file. Is this some kind of buffering mechanism? Do I need to use lazy-cat here? If so, how can I apply a macro to a sequence as if it were variadic arguments?
(defn char-seq [rdr]
(let [coll (line-seq rdr)]
(apply concat (map (fn [x] (println \,) x) coll))))
(def tmp (char-seq (clojure.contrib.io/reader file)))
;,
;,
;,
;,
#'user/tmp
Part of what you're seeing is due to apply, since it will need to realize as many args as needed by the function definition. E.g.:
user=> (defn foo [& args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
#'user/bar
user=> (defn foo [x & args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
2
#'user/bar
user=> (defn foo [x y & args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
2
3
#'user/bar
What I want to do is like following.
(def mystream (stream (range 100)))
(take 3 mystream)
;=> (0 1 2)
(take 3 mystream)
;=> (3 4 5)
(first (drop 1 mystream))
;=> 7
The stream function make sequence side-effectfull like io stream.
I think this is almost impossible.
Here is my attempt.
(defprotocol Stream (first! [this]))
(defn stream [lst]
(let [alst (atom lst)]
(reify Stream
(first! [this]
(let [[fs] #alst]
(swap! alst rest)
fs)))))
(let [mystream (stream (iterate inc 1))]
(map #(if (string? %) (first! mystream) %)
[:a "e" "b" :c "i" :f]))
;=> (:a 1 2 :c 3 :f)
Unfotunately this approach need to implement all function I will use.
Judging by your followup comment to Maurits, you don't need mutation, but rather simply need to emit a new sequence with the elements in the right place.
For example:
(defn replace-when [pred coll replacements]
(lazy-seq
(when (seq coll)
(if (seq replacements)
(if (pred (first coll))
(cons (first replacements)
(replace-when pred (rest coll) (rest replacements)))
(cons (first coll)
(replace-when pred (rest coll) replacements)))
coll))))
user=> (def seq1 [:a :b :c])
#'user/seq1
user=> (def seq2 [:x "i" "u" :y :z "e"])
#'user/seq2
user=> (replace-when string? seq2 seq1)
(:x :a :b :y :z :c)
This won't work with the standard take and drop, but you could quite easily write your own to work on a mutable atom, e.g. you could do something like this:
(def mystream (atom (range 100)))
(defn my-take [n stream]
(let [data #stream
result (take n data)]
(reset! stream (drop n data))
result))
(my-take 3 mystream)
=> (0 1 2)
(my-take 3 mystream)
=> (3 4 5)