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.
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:
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 builtreduce
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 wantI 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.