Clojure newbie here, I was going through the excellent "Clojure from the ground up" posts, and tried out the last exercise in this post.
When I replace alter with commute, the sum is inaccurate, but I don't understand why.
(def work (ref (apply list (range 1e5))))
(def sum (ref 0))
(defn trans-alter [work sum]
(dosync
(if-let [n (first #work)]
(do
(alter work rest)
(alter sum + n)
(count #work))
0)))
(defn trans-commute [work sum]
(dosync
(if-let [n (first #work)]
(do
(commute work rest)
(commute sum + n)
(count #work))
0)))
(I've skipped the code that sets up the futures and calls them etc)
With trans-alter here I got 4999950000 for the sum (which is the correct expected value), while with trans-commute I got a different value each time, but higher than expected (e.g. 4999998211).
What am I missing here? Thanks in advance!
Commute and alter essentially do the same thing, though commute is a little more lenient on the guarantee of correctness.
Alter instructs the STM to always ensure that this code ran all the way through without any of the refs it uses changing out from under it.
Commute is an instruction to help the STM decide when it needs to abort a transaction because the underlying data changed out from under it.
If everything in a transaction is commutative, then it's ok to let that transaction finish even if some data changed. In your case two transactions could both:
grab the first number
remove the same number from work
add the same number to result
then use commute to instruct the STM that this is OK, and it should just go ahead and commit the transaction anyway...
get the wrong answer.
So in short,the work you are asking to preform is not actually a commutative operation. Specifically removing an item from a list is not commutative. If you change any of the commutes to an alter, then step 4 would have kicked one of them out and only allowed one of them to finish. The one that got kicked out would be re-run on the fresh data and eventually would have arrived at a correct result.
Related
for an assignment I need to create a map from a text file in clojure, which I am new to. I'm specifically using a hash-map...but it's possible I should be using another type of map. I'm hoping someone here can answer that for me. I did try changing my hash-map to sorted-map but it gave me the same problem.
The first character in every line in the file is the key and the whole line is the value. The key is a number from 0-9999. There are 10,000 lines and each number after the first number in a line is a random number between 0 and 9999.
I've created the hashmap successfully I think. At least, its not giving me an error when I just run that code. However when I try to iterate through it, printing every value for keys 0-9999 it gives me a stack overflow error right at the middle of line 2764(in the text file). I'm hoping someone can tell me why it's doing this and a better way to do it?
Here's my code:
(ns clojure-project-441.core
(:gen-class))
(defn -main
[& args]
(def pages(def hash-map (file)))
(iter 0)
)
(-main)
(defn file []
(with-open [rdr (clojure.java.io/reader "pages.txt")]
(reduce conj [] (line-seq rdr))))
(defn iter [n]
(doseq [keyval (pages n)] (print keyval))
(if (< n 10000)
(iter (inc n))
)
)
here's a screenshot of my output
If it's relevant at all I'm using repl.it as my IDE.
Here are some screenshots of the text file, for clarity.
beginning of text file
where the error is being thrown
Thanks.
I think the specific problem that causes the exception to be thrown is caused because iter calls itself recursively too many times before hitting the 10,000 line limit.
There some issues in your code that are very common to all people learning Clojure; I'll try to explain:
def is used to define top-level names. They correspond with the concept of constants in the global scope on other programming languages. Think of using def in the same way you would use defn to define functions. In your code, you probably want to use let to give names to intermediate results, like:
(let [uno 1
dos 2]
(+ uno dos)) ;; returns 3
You are using the name hash-map to bind it to some result, but that will get in the way if you want to use the function hash-map that is used to create maps. Try renaming it to my-map or similar.
To call a function recursively without blowing the stack you'll need to use recur for reasons that are a bit long to explain. See the factorial example here: https://clojuredocs.org/clojure.core/recur
My advice would be to think of this assignment as a pipeline composed of the following small functions:
A function that reads the lines from the file (you already have this)
A function that, given a line, returns a pair: the first element of the pair is the first number of the line, the second element is the whole line (the input parameter) OR
A function that reads the first number of the line
To build the map, you have a few options; two off the top of my mind:
Use a loop construct and, for each line, "update" the hash-map to include a new key-value pair (the key is the first number, the value is the whole line), then return the whole hash-map you've built
Use a reduce operation: you create a collection of key-value pairs, then tell reduce to merge, one step at a time, into the original hash-map. The result is the hash-map you want
I think the key is to get familiar with the functions that you can use and build small functions that you can test in isolation and try to group them conveniently to solve your problem. Try to get familiar with functions like hash-map, assoc, let, loop and recur. There's a great documentation site at https://clojuredocs.org/ that also includes examples that will help you understand each function.
I'm building a ClojureScript app and I'm having trouble with using reagent to fill a table with data. The two issues I'm having are TONS of warnings of the form
Every element in a seq should have a unique :key
And also, as soon as I call the function that does the rendering, it renders correctly, then my entire page freezes and reloading the page is the only way to fix it. These are my two functions:
(defn foo
[]
[:table
(for [i (range 10)]
[:tr (for [j (range 3)]
[:td (str "Row " i ", Col " j)])])])
And when I call the following, I get the warnings and the page freezes, though it does render correctly:
(reagent/render [foo] (dom/getElement "results"))
Am I approaching the process of filling in data the wrong way? Is there an easier way?
The warning you are getting is due to reagent requiring a unique key value for dynamic elements created along these lines. There are a couple of ways you can fix this. The other thing you need to watch out for is possible issues with using for because it generates lazy sequences. While this is working for you in this context, it can create subtle issues with re-rendering.
My advice would be to create a function to render the td element and prefix the rendering with
^{:key (str i j)} [:td (str "Row " i ", Col " j)]
The (str i j) will create a unique key for each td element. The other thing I find useful is to use into i.e.
(into [:tr]
(for [j (range 3)]
^{:key (str i j)} [:td ....])))
I've been developing my own app using reagent. It isn't great code and it still needs a lot of re-factoring, but I have done tables like this as well as paginated tables and a few other reagent components, such as tabs, sidebar menus etc. It can be found at my github arcis project It should give you some ideas if nothing else
There is also some good documentation regarding reagent and how it does rendering and some of the subtle 'gotchas' at re-frame wiki
I am working through the first edition of this book and while I enjoy it, some of the examples given seem out-dated. I would give up and find another book to learn from, but I am really interested in what the author is talking about and want to make the examples work for myself, so I am trying to update them as I go along.
The following code is a map/reduce approach to analyzing text that depends on clojure.contrib. I have tried changing the .split function to re-seq with #"\w+", used line-seq instead of read-lines, and changed the .toLowerCase to string/lower-case. I tried to follow my problems to the source code and read the docs thoroughly to learn that the read-lines function closes after you consume the entire sequence and that line-seq returns a lazy sequence of strings, implementing java.io.BufferedReader. The most helpful thing for my problem was post about how to read files after clojure 1.3. Even still, I can't get it to work.
So here's my question: What dependencies and/or functions do I need to change in the following code to make it contemporary, reliable, idiomatic Clojure?
First namespace:
(ns chapter-data.word-count-1
(:use clojure.contrib.io
clojure.contrib.seq-utils))
(defn parse-line [line]
(let [tokens (.split (.toLowerCase line) " ")]
(map #(vector % 1) tokens)))
(defn combine [mapped]
(->> (apply concat mapped)
(group-by first)
(map (fn [[k v]]
{k (map second v)}))
(apply merge-with conj)))
(defn map-reduce [mapper reducer args-seq]
(->> (map mapper args-seq)
(combine)
(reducer)))
(defn sum [[k v]]
{k (apply + v)})
(defn reduce-parsed-lines [collected-values]
(apply merge (map sum collected-values)))
(defn word-frequency [filename]
(map-reduce parse-line reduce-parsed-lines (read-lines filename)))
Second namespace:
(ns chapter-data.average-line-length
(:use rabbit-x.data-anal
clojure.contrib.io))
(def IGNORE "_")
(defn parse-line [line]
(let [tokens (.split (.toLowerCase line) " ")]
[[IGNORE (count tokens)]]))
(defn average [numbers]
(/ (apply + numbers)
(count numbers)))
(defn reducer [combined]
(average (val (first combined))))
(defn average-line-length [filename]
(map-reduce parse-line reducer (read-lines filename)))
But when I compile and run it in light table I get a bevy of errors:
1) In the word-count-1 namespace I get this when I try to reload the ns function after editing:
java.lang.IllegalStateException: spit already refers to: #'clojure.contrib.io/spit in namespace: chapter-data.word-count-1
2) In the average-line-length namespace I get similar name collision errors under the same circumstances:
clojure.lang.Compiler$CompilerException: java.lang.IllegalStateException: parse-line already refers to: #'chapter-data.word-count-1/parse-line in namespace: chapter-data.average-line-length, compiling:(/Users/.../average-line-length.clj:7:1)
3) Oddly, when I quit and restart light table, copy and paste the code directly into the files (replacing what's there) and call instances of their top level functions the word-count-1 namespace runs fine, giving me the number of occurrences of certain words in the test.txt file but the average-line-length name-space gives me this:
"Warning: *default-encoding* not declared dynamic and thus is not dynamically rebindable, but its name suggests otherwise. Please either indicate ^:dynamic *default-encoding* or change the name. (clojure/contrib/io.clj:73)...
4) At this point when I call the word-frequency functions of the first namespace it returns nil instead of the number of word occurrences and when I call the average-line-length function of the second namespace it returns
java.lang.NullPointerException: null
core.clj:1502 clojure.core/val
As far as I can tell, clojure.contrib.io and clojure.contrib.seq-utils are no longer updated, and in fact they may be conflicting with clojure.core functions like spit. I would recommend taking out those dependencies and seeing if you can do this using only core functions. spit should just work -- the error that you're getting is caused by useing clojure.contrib.io, which contains its own spit function, which looks to be roughly equivalent; perhaps the current version in clojure.core is a "new and improved" version of clojure.contrib.io/spit.
Your problem with the parse-line function looks to be caused by the fact that you've defined two functions with the same name, in two different namespaces. The namespaces don't depend on one another, but you can still run into a conflict if you load both namespaces in a REPL. If you only need to use one at a time, try using one of them, and then when you want to use the other one, make sure you do a (remove-ns name-of-first-ns) first to free up the vars so there is no conflict. Alternatively, you could make parse-line a private function in each namespace, by changing (defn parse-line ... to (defn- parse-line ....
EDIT: If you still need any functions that were in clojure.contrib.io or clojure.contrib.seq-utils that aren't available in core or elsewhere, you can always copy the source over into your namespace. See clojure.contrib.io and clojure.contrib.seq-utils on github.
Given that the STM holds a history of say 10 values of refs, agents etc, can those values be read ?
The reason is, I'm updating a load of agents and I need to keep a history of values. If the STM is keeping them already, I'd rather just use them. I can't find functions in the API that look like they read values from the STM history so I guess not, nor can I find any methods in the java source code, but maybe I didn't look right.
You cannot access the stm history of values directly. But you can make use of add-watch to record the history of values:
(def a-history (ref []))
(def a (agent 0))
(add-watch a :my-history
(fn [key ref old new] (alter a-history conj old)))
Every time a is updated (the stm transaction commits) the old value will be conjed onto the sequence that is held in a-history.
If you want to get access to all the intermediary values, even for rolled back transactions you can send the values to an agent during the transaction:
(def r-history (agent [])
(def r (ref 0))
(dosync (alter r
(fn [new-val]
(send r-history conj new-val) ;; record potential new value
(inc r)))) ;; update ref as you like
After the transaction finished, all changes to the agent r-history will be executed.
So I am using congomongo (the fetch function near the end) to pull some documents from a mongodb collection. I want to pass options through to the fetch call, so i can do something like (posts :limit 1) and have {:limit 1} get passed through to fetch. I am doing hand rolled "memoization" with #posts, because i want to be able to reset the cache, which to my understanding cant be done with clojure.core/memoize.
Now, the problem I see here is that (fetch :posts options) call is non trivial, and I would really rather not hammer my datastore if dosync has to retry the transaction. I am a total clojure/fp noob though, and I am not sure how to get around that problem. Also, since I am a noob, if I am doing anything else here that makes you cringe, I would love to find out how to write this properly.
(def posts (ref nil))
(defn reset-posts [] (dosync alter posts nil))
(defn fetch-posts [& options]
(let [options (apply array-map options)]
(or #posts
(dosync alter posts (fetch :posts options)))))
I'm not convinced that your transaction blocks ((dosync alter...) do what you think!
user=> (def posts (ref nil))
#'user/posts
user=> (dosync (ref-set posts [1 2 3 4 5]))
[1 2 3 4 5]
user=> #posts
[1 2 3 4 5]
user=> (dosync alter posts nil)
nil
user=> #posts
[1 2 3 4 5]
In reset-posts, you probably want (dosync (ref-set posts nil)), and in fetch-posts, the syntax fix would be (dosync (ref-set posts (fetch :posts options))).
However, there's a race condition in fetch-posts, a check-then-act. Might not be that big of a deal; not sure who uses fetch-posts, but moving the or #posts bit inside the transaction would avoid a situation where 2 concurrent transactions both end up committing the alter.
With regard to retries of fetch-posts, yeah, that could happen, though your cache solution avoids most of them. I'm not sure there's a way around it without locking, though. Usually with I/O stuff in transactions you'd farm it out to an agent, but the transaction's success depends on the return value from fetch, so it's not clear to me how that'd work.
So you're introducing the ref because you want to be able to not blow up memory when time passes, 'cause just using memoize around fetch-posts may lead to this, sooner or later, right ?
Maybe you could try an alternate approach : let fetch-posts be "pure", memoize-free. With this scenario, someone can call fetch-posts blindlessly, without having to fear OutOfMemoryExceptions.
Indeed, maybe for some usecases, it may be sufficient to "cache the value" in a local of the calling code.
But the story does not end here, or I would not have taken the time to answer :-) : you can pretty easily have your "localized in time" memoize by rebinding fetch-posts using clojure.core/binding : from then, all the code in the same thread in the call stack will benefit from the bound memoized fetch-posts.
If you're using clojure 1.3 alpha, you'll need to declare the fetch-posts var as rebindable explicitly via the :dynamic metadata.
;; most simple definition
(defn ^:dynamic fetch-posts [& options]
(let [options (apply array-map options)]
(fetch :posts options)))
;; a la carte caching by the calling code (lexically scoped)
(let [posts (apply fetch-posts options)] ...)
;; a la carte caching by the calling code (dynamically scoped)
(binding [fetch-posts (memoize fetch-posts)] ...)
My last guess would be that you'd want to "memoize" in posts, in your original version, by indexing the posts by a key which would be the options seq, right ? Some maybe your code was not right ? (or you made the assumption that fetch-posts would always be called with the same args over and over ?)
Another idea. Use an agent to serialize write-access to posts and then ensure the call to fetch is only done when it is nil :
(def posts (agent nil))
(defn reset-posts [] (send posts (constantly nil)))
(defn fetch-posts [& options]
(let [options (apply array-map options)]
(send-off posts #(or % (fetch :posts options)))
(await-for (Long/MAX_VALUE) posts)
#posts))
Another approach which might be useful to move extensive computations outside the dosync is to use delay.
(defn fetch-posts
[& options]
#(dosync (or #posts (ref-set posts (delay (apply fetch :posts options))))))
Also note that your original code is not thread-safe since you access the ref outside the dosync and modify it based on this value afterwards in the dosync. But the value might have changed already between the deref and the dosync. Eg. by another thread calling fetch-posts in parallel.
Also the agent approach is questionable, because you cannot reliably read an agent. The value you get is consistent, but the access is not synchronised. Consider Laurent's example: between await-for and the deref another thread might already call reset-posts and you get nil instead of the post data. In this example this is probably a) far fetched and b) maybe a case one has to consider anyway, but there might be other use cases where this introduces a subtle race condition in more critical code.
tl;dr: Be careful what you do! Clojure is not magically thread-safe. Reason thoroughly about your solution and be aware of the implications.