Monty Hall and Lazy Sequences (?) - clojure

I'm just starting to learn Clojure and I'm trying to simulate the Monty Hall problem:
(defn create-contest
"Creates a monty hall doors contest"
[n]
(do (def door-with-car (rand-int n))
(map
(fn [i] (if (= i door-with-car) :car :closed))
(range n))))
(defn create-doors
"Create a collection of monty hall contests"
[doors contests]
(doall
(map
(fn [i] (create-contest i))
(repeat contests doors))))
But every time I execute the create-doors function all the doors with cars end up in the same position:
broker.core> (create-doors 4 10)
((:car :closed :closed :closed)
(:car :closed :closed :closed)
(:car :closed :closed :closed)
(:car :closed :closed :closed)
(:car :closed :closed :closed)
(:car :closed :closed :closed)
(:car :closed :closed :closed)
(:car :closed :closed :closed)
(:car :closed :closed :closed)
(:car :closed :closed :closed))
broker.core> (create-doors 4 10)
((:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed))
broker.core> (create-doors 4 10)
((:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed))
What am I doing wrong?

You're very close, you should just use a let form instead of def here:
(defn create-contest
"Creates a monty hall doors contest"
[n]
(let [door-with-car (rand-int n)] ;; let instead of def
(map
(fn [i] (if (= i door-with-car) :car :closed))
(range n))))
def will bind some value to a var in the current namespace, but you want to bind a value in the scope of this function, and that's what let is good for. See this Q&A for another explanation.
(defn create-doors
"Create a collection of monty hall contests"
[doors contests]
(map
create-contest ;; no need to wrap create-contest in another function here
(repeat contests doors)))
You don't need to wrap create-contest in another function for use with map — you can just pass the function directly to map as a value. The doall is only necessary to force realization of the lazy sequence from map, so you probably don't need/want that inside create-doors.
(create-doors 4 10)
=>
((:closed :closed :car :closed)
(:closed :closed :closed :car)
(:closed :car :closed :closed)
(:closed :closed :closed :car)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :car :closed :closed)
(:closed :closed :closed :car)
(:closed :closed :car :closed)
(:closed :closed :car :closed))

Following on from #TaylorWood's solution, you can express both your functions more concisely, though I'm not sure the concise versions are as clear.
Your create-contest's map's function tests every number i to see if it is the one. Better just change the one directly. To do that, we need a vector, which I think you'll want to look up anyway when simulating the game:
(defn create-contest [n]
(assoc (vec (repeat n :closed)) (rand-int n) :car))
Your create-doors function repeatedly passes the same argument to its map's function. You can use repeatedly on a closure to get the same effect:
(defn create-doors [doors contests]
(repeatedly contests (partial create-contest doors)))
Or
(defn create-doors [doors contests]
(repeatedly contests #(create-contest doors)))

Related

Clojure spec for dependencies between nodes in a recursively defined data structure

Is there a canonical or idiomatic way of writing a spec for dependencies between the values in nodes in a recursively defined data structure?
As a minimal example, let's say I want to store a sorted list as a nested vector where each "node" is a value and the tail of the list:
[1 [2 [3 [4 nil]]]]
The spec for the structure of the list itself can be written
(s/def ::node (s/or :empty nil?
:list (s/cat :value number? :tail ::node)))
However, when I want to add the ordering requirement I cannot find a good way of
writing it.
The straight-forward way of writing it feels a bit clumsy. As the conformed
value of :tail is a MapEntry, I cannot use something like (get-in % [:tail :list :value])
(I could write it as (get-in % [:tail 1 :value]) but that hard-coded index feels too brittle),
but have to thread it through (val):
(s/def ::node (s/or :empty nil?
:list (s/& (s/cat :value number? :tail ::node)
#(or (= (-> % :tail key) :empty)
(< (:value %) (-> % :tail val :value)))
)))
This works:
(s/conform ::node nil) ; [:empty nil]
(s/conform ::node [1 nil ] ) ; [:list {:value 1, :tail [:empty nil]}]
(s/explain ::node [4 [1 nil]] )
; {:value 4, :tail [:list {:value 1, :tail [:empty nil]}]} - failed:
; (or (= (-> % :tail key) :empty) (< (:value %) (-> % :tail val
; :value))) in: [1] at: [:list] spec: :spec-test.core/node
; [4 [1 nil]] - failed: nil? at: [:empty] spec: :spec-test.core/node
(s/conform ::node [1 [4 nil]] ) ; [:list {:value 1, :tail [:list {:value 4, :tail [:empty nil]}]}]
(s/conform ::node [1 [2 [4 nil]]] )
; [:list
; {:value 1,
; :tail
; [:list {:value 2, :tail [:list {:value 4, :tail [:empty nil]}]}]}]
Alternatively, I can use a multi-spec to make the spec for ::node
a bit clearer:
(s/def ::node (s/or :empty nil?
:list (s/& (s/cat :value number? :tail ::node)
(s/multi-spec empty-or-increasing :ignored)
)))
That also allows me to separate out the :empty branch but it still has the problem of getting the value of the (head of the) :tail:
(defmulti empty-or-increasing #(-> % :tail key))
(defmethod empty-or-increasing :empty
[_]
(fn[x] true))
(defmethod empty-or-increasing :default
[_]
#(do (< (:value %) (-> % :tail val :value)))
)
Is there a way to get the :value of the :tail node without having to
extract the val part of the MapEntry with #(-> % :tail val :value)
or #(get-in % [:tail 1 :value])?
You could use s/conformer in order to get a map in lieu of a MapEntry.
(s/def ::node (s/and (s/or :empty nil?
:list (s/& (s/cat :value number? :tail ::node)
(fn [x]
(or (-> x :tail (contains? :empty))
(-> x :tail :list :value (> (:value x)))))))
(s/conformer (fn [x] (into {} [x])))))
and the result will look somewhat more consistent:
(s/conform ::node [1 [2 [4 nil]]])
=> {:list {:value 1, :tail {:list {:value 2, :tail {:list {:value 4, :tail {:empty nil}}}}}}}

Clojure convert vector of maps into map of vector values

There are a few SO posts related to this topic, however I could not find anything that works for what I am looking to accomplish.
I have a vector of maps. I am going to use the example from another related SO post:
(def data
[{:id 1 :first-name "John1" :last-name "Dow1" :age "14"}
{:id 2 :first-name "John2" :last-name "Dow2" :age "54"}
{:id 3 :first-name "John3" :last-name "Dow3" :age "34"}
{:id 4 :first-name "John4" :last-name "Dow4" :age "12"}
{:id 5 :first-name "John5" :last-name "Dow5" :age "24"}]))
I would like to convert this into a map with the values of each entry be a vector of the associated values (maintaining the order of data).
Here is what I would like to have as the output:
{:id [1 2 3 4 5]
:first-name ["John1" "John2" "John3" "John4" "John5"]
:last-name ["Dow1" "Dow2" "Dow3" "Dow4" "Dow5"]
:age ["14" "54" "34" "12" "24"]}
Is there an elegant and efficient way to do this in Clojure?
Can be made more efficient, but this is a nice start:
(def ks (keys (first data)))
(zipmap ks (apply map vector (map (apply juxt ks) data))) ;;=>
{:id [1 2 3 4 5]
:first-name ["John1" "John2" "John3" "John4" "John5"]
:last-name ["Dow1" "Dow2" "Dow3" "Dow4" "Dow5"]
:age ["14" "54" "34" "12" "24"]}
Another one that comes close:
(group-by key (into [] cat data))
;;=>
{:id [[:id 1] [:id 2] [:id 3] [:id 4] [:id 5]],
:first-name [[:first-name "John1"] [:first-name "John2"] [:first-name "John3"] [:first-name "John4"] [:first-name "John5"]],
:last-name [[:last-name "Dow1"] [:last-name "Dow2"] [:last-name "Dow3"] [:last-name "Dow4"] [:last-name "Dow5"]],
:age [[:age "14"] [:age "54"] [:age "34"] [:age "12"] [:age "24"]]}
Well, I worked out a solution and then before I could post, Michiel posted a more concise solution, but I'll go ahead and post it anyway =).
(defn map-coll->key-vector-map
[coll]
(reduce (fn [new-map key]
(assoc new-map key (vec (map key coll))))
{}
(keys (first coll))))
For me, the clearest approach here is the following:
(defn ->coll [x]
(cond-> x (not (coll? x)) vector))
(apply merge-with #(conj (->coll %1) %2) data)
Basically, the task here is to merge all maps (merge-with), and gather up all the values at the same key by conjoining (conj) onto the vector at key – ensuring that values are actually conjoined onto a vector (->coll).
If we concatenate the maps into a single sequence of pairs, we have an edge-list representation of a graph. What we have to do is convert it into an adjacency-list (here a vector, not a list) representation.
(defn collect [maps]
(reduce
(fn [acc [k v]] (assoc acc k (conj (get acc k []) v)))
{}
(apply concat maps)))
For example,
=> (collect data)
{:id [1 2 3 4 5]
:first-name ["John1" "John2" "John3" "John4" "John5"]
:last-name ["Dow1" "Dow2" "Dow3" "Dow4" "Dow5"]
:age ["14" "54" "34" "12" "24"]}
The advantage of this method over some of the others is that the maps in the given sequence can be different shapes.
Please consider the reader when writing code! There are no prizes for playing "code golf". There are, however, considerable costs to others when you force them to decipher code that is overly condensed.
I always try to be explicit about what code is doing. This is easiest if you break down a problem into simple steps and use good names. In particular, it is almost impossible to accomplish this using juxt or any other cryptic function.
Here is how I would implement the solution:
(def data
[{:id 1 :first-name "John1" :last-name "Dow1" :age "14"}
{:id 2 :first-name "John2" :last-name "Dow2" :age "54"}
{:id 3 :first-name "John3" :last-name "Dow3" :age "34"}
{:id 4 :first-name "John4" :last-name "Dow4" :age "12"}
{:id 5 :first-name "John5" :last-name "Dow5" :age "24"}])
(def data-keys (keys (first data)))
(defn create-empty-result
"init result map with an empty vec for each key"
[data]
(zipmap data-keys (repeat [])))
(defn append-map-to-result
[cum-map item-map]
(reduce (fn [result map-entry]
(let [[curr-key curr-val] map-entry]
(update-in result [curr-key] conj curr-val)))
cum-map
item-map))
(defn transform-data
[data]
(reduce
(fn [cum-result curr-map]
(append-map-to-result cum-result curr-map))
(create-empty-result data)
data))
with results:
(dotest
(is= (create-empty-result data)
{:id [], :first-name [], :last-name [], :age []})
(is= (append-map-to-result (create-empty-result data)
{:id 1 :first-name "John1" :last-name "Dow1" :age "14"})
{:id [1], :first-name ["John1"], :last-name ["Dow1"], :age ["14"]})
(is= (transform-data data)
{:id [1 2 3 4 5],
:first-name ["John1" "John2" "John3" "John4" "John5"],
:last-name ["Dow1" "Dow2" "Dow3" "Dow4" "Dow5"],
:age ["14" "54" "34" "12" "24"]}))
Note that I included unit tests for the helper functions as a way of both documenting what they are intended to do as well as demonstrating to the reader that they actually work as advertised.
This template project can be used to run the above code.

How to retrieve element by key after using instaparse in clojure?

I am trying to make a compiler for a school project. I am a beginner in clojure. I have done a simple program (interpreting-lang-if) which can parse a string using instaparse and return a vector like this :
[:LangIF [:before_if "676767; "] [:condition_expression "0"]
[:statements_OK "1; 2;"] [:statements_NOK "3+4;"] [:after_if ""]]
How can I get the "before_if" element from the list?
I tried to understand the get function but I must have minsunderstood something in the usage of the function.
Here is some code :
(prn (interpreting-lang-if "676767; if (0) {1; 2;}else{3+4;};"))
(get (interpreting-lang-if "676767; if (0) {1; 2;}else{3+4;};") :before_if)
(get :before_if (interpreting-lang-if "676767; if (0) {1; 2;}else{3+4;};"))
The expected output is supposed to be "676767" instead of nil.
Thanks for your help.
If you dont know the exact position of the item:
(->> [:LangIF [:before_if "676767; "] [:condition_expression "0"]
[:statements_OK "1; 2;"] [:statements_NOK "3+4;"] [:after_if ""]]
(tree-seq vector? rest)
(filter vector?)
(filter (comp (partial = :before_if) first))
(first)
(second))
or if you do and would like to use specter:
(let [A [:LangIF [:before_if "676767; "] [:condition_expression "0"]
[:statements_OK "1; 2;"] [:statements_NOK "3+4;"] [:after_if ""]]]
(select [1 1] A))
or with simple get:
(let [A [:LangIF [:before_if "676767; "] [:condition_expression "0"]
[:statements_OK "1; 2;"] [:statements_NOK "3+4;"] [:after_if ""]]]
(get (get A 1) 1))
I've found zippers to be useful with Instaparse ASTs, especially when you need to find a particular node then find another node relative to it. Here's a function that searches through nodes to find the one matching a predicate:
(require '[clojure.zip :as zip])
(defn zip-to
([loc pred direction]
(loop [loc loc]
(if (and loc (not (zip/end? loc)))
(if (pred (zip/node loc))
loc
(recur (direction loc)))
loc))))
To find :before_if in your AST:
(-> [:LangIF
[:before_if "676767; "]
[:condition_expression "0"]
[:statements_OK "1; 2;"]
[:statements_NOK "3+4;"]
[:after_if ""]]
zip/vector-zip
(zip-to #{:before_if} zip/next)
zip/right
zip/node)
You can easily manipulate tree-like data-structures using the Tupelo Forest library. Here is a live example of your problem:
(dotest
(with-forest (new-forest)
(let [data-hiccup [:LangIF
[:before_if "676767; "]
[:condition_expression "0"]
[:statements_OK "1; 2;"]
[:statements_NOK "3+4;"]
[:after_if ""]]
root-hid (add-tree-hiccup data-hiccup)
before-hid (find-hid root-hid [:LangIF :before_if])
before-node (hid->node before-hid)
before-value (grab :value before-node)]
(is= before-node {:tupelo.forest/khids [], :tag :before_if, :value "676767; "})
(is= before-value "676767; "))))
before-hid is a pointer to the desired node, which we find by specifying the desired path [:LangIF :before_if] from the root node. We can then convert the pointer into the entire node, and extract the :value from the node. Many further manipulations are possible. See the docs and more examples.

Getting date intervals from vector

I have a day ordered vector in clojure that's something like
(def a [{:day #inst "2017-01-01T21:57:14.873-00:00" :balance 100.00},
{:day #inst "2017-01-05T21:57:14.873-00:00" :balance -50.00},
{:day #inst "2017-01-10T21:57:14.873-00:00" :balance -100.00},
{:day #inst "2017-01-14T21:57:14.873-00:00" :balance 50.00},
{:day #inst "2017-01-17T21:57:14.873-00:00" :balance -200.00}])
I would like to get all date intervals where balance is negative. The
period ends when balance gets positive on next position or when
balance changes its value but keeps negative, like:
[{:start #inst "2017-01-05T21:57:14.873-00:00"
:end #inst "2017-01-09T21:57:14.873-00:00"
:value -50.00},
{:start "2017-01-10T21:57:14.873-00:00"
:end "2017-01-13T21:57:14.873-00:00"
:value -100.00},
{:start "2017-01-17T21:57:14.873-00:00"
:value -200.00}]
I've found this and this but I couldn't adapt to my data. How can I do it?
Cheating a little with the dates by using this not yet implemented function, which is supposed to decrement a date:
(defn dec-date [d] d)
This would be one way to solve the problem:
(->> a
(partition-by #(-> % :balance neg?))
(drop-while #(-> % first :balance pos?))
(mapcat identity)
(map (juxt :day :balance))
(partition 2 1)
(map (fn [[[date-1 val-1] [date-2 val-2]]]
(if (neg? val-1)
{:start date-1
:end (dec-date date-2)
:value val-1}
{:start date-2
:value val-1})))))
;;=> ({:start "05/01", :end "10/01", :value -50.0} {:start "10/01", :end "14/01", :value -100.0} {:start "17/01", :value 50.0})
If dec-date was properly implemented then the first :end would be "09/01" rather than "10/01" and the second :end would be "13/01" rather than "14/01", which would be the correct answer.
Now hopefully an improved answer that will work for more edge cases:
(->> a
(partition-by #(-> % :balance neg?))
(drop-while #(-> % first :balance pos?))
(mapcat identity)
(map (juxt :day :balance))
(partition-all 2 1)
(keep (fn [[[date-1 val-1] [date-2 val-2]]]
(cond
(neg? val-1) (cond-> {:start date-1
:value val-1}
date-2 (assoc :end (dec-date date-2)))
(pos? val-1) nil
:else {:start date-2
:value val-1}))))

Clojure: Cycle through a collection while looping through another collection?

I have two collections x and y, both having different number of items. I want to loop through x and do something with a side-effect, while cycling through y. I don't want to repeat y while looping through x. Both doseq and for repeats y:
(for [x (range 5)
y ["A" "B"]]
[x y])
This produces ([0 "A"] [0 "B"] [1 "A"] [1 "B"] [2 "A"] [2 "B"] [3 "A"] [3 "B"] [4 "A"] [4 "B"]).
What I want is something that will produce: ([0 "A"] [1 "B"] [2 "A"] [3 "B"] [4 "A"]).
Background, I have lines from a file and core.async channels (say 5) and I want to put each line to the next channel in my collection, something like:
(defn load-data
[file chans]
(with-open [rdr (io/reader file)]
(go
(doseq [l (line-seq rdr)
ch chans]
(>! ch l)))))
If you pass multiple sequences to map it steps through each of them in lock step calling the mapped function with the value from the current position in each. Stopping when one of the sequences runs out.
user> (map vector (range 5) (cycle ["A" "B"]))
([0 "A"] [1 "B"] [2 "A"] [3 "B"] [4 "A"])
In this case the sequence from (cycle ["A" "B"]) will keep producing As and Bs forever though map will stop consuming them when the sequence from (range 5) ends. each step then calls the vector function with these two arguments and adds the result to the output sequence.
and for the second example using a go-loop is a fairly standard way of fanning out an input sequence:
user> (require '[clojure.core.async :refer [go go-loop <! <!! >!! >! chan close!]])
nil
user> (defn fanout [channels file-lines]
(go-loop [[ch & chans] (cycle channels)
[line & lines] file-lines]
(if line
(do
(>! ch line)
(recur chans lines))
(doseq [c channels]
(close! c)))))
#'user/fanout
user> (def lines ["first" "second" "third" "fourth" "fifth"])
#'user/lines
user> (def test-chans [(chan) (chan) (chan)])
#'user/test-chans
user> (fanout test-chans lines)
#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel#3b363fc5>
user> (map <!! test-chans)
("first" "second" "third")
user> (map <!! test-chans)
("fourth" "fifth" nil)