I am trying to save data into a collection of some sort, but the program that I have is saving everything into a separate map. I want to make it one map.
(defn readFile []
(map (fn [line] (clojure.string/split line #";"))
(with-open [rdr (reader "C:/Users/Rohil/Desktop/textfile.txt")]
(doseq [[idx line] (map-indexed vector(line-seq rdr))]
(if(.contains line "201609")
(if(not(.contains line "TBA"))
(println(assoc table :code(nth(clojure.string/split line #";")3) :instructor(nth(clojure.string/split line #";")19)))
)
)
)
)
)
)
)
Any help will be appreciated.
Looks like you are adapting to clojure :-) I went to the same process. Hang on, it will be worth it!
First: it is important to realize that map will save the result of the function into a new collection. Like cfrick mentions, println returns nil and assoc does not change a map.
I'm guessing a bit here what you are trying to do: You want to have a collection of dicts, where every dict has two keys, like so:
[
{ :code 1 :instructor "blah"}
{ :code 2 :instructor "boo" }
]
You need these values to come from a file, but you only want to save the lines where the line contains "201609" but not "TBA"
First some general remarks:
You probably want to split this function into smaller parts. One could be the check for lines (contains 201609 but not tba ), another could read the file...
I know it is the title of your question, but most likely there is a better way than to change a global variable. Maybe you could make the function readFile return the table?
try if you can pass in arguments to your function.
I'm not sure what you are trying to do with the line (doseq [[... Please give us more context there. I will ignore it
Here is a possible solution:
(ns test
(:require [clojure.string :as s]
[clojure.java.io :as io]))
(defn line-filter [include exclude line]
(and (not (s/includes? line exclude))
(s/includes? line include)))
(defn process-line [line]
(let [line-parts (s/split line #";")
code (nth line-parts 3)
instructor (nth line-parts 19)]
{:code code :instructor instructor}))
(defn read-file [file-name]
(s/split (slurp (io/resource file-name)) #"\n"))
(defn parse-lines [lines]
(map process-line lines))
(defn read-file-and-parse
"This function will read a file, process the lines, and output a collection of maps"
[filename search-for exclude]
(parse-lines
(filter #(line-filter search-for exclude %)
(read-file filename))))
you could now call this function like this: (read-file-and-parse "test.txt" "201609" "TBA")
If you want to add the result of this function into your table, you can use concat. But again, this will return a new version of your list (with new entries added) and not change the one you defined earlier.
Welcome to functional programming :-)))
Related
I am trying to develop a converter that takes an input string and converts it into morse code through a Morse Library map, while also respecting functional programming rules. Sorry for any clarification issues, I am new to Stack Overflow
(ns clojureassignment.core
(:gen-class))
(require '[clojure.string :as str])
;this function is where the converter is developed
(defn morse->ASCI
[x]
(def morse_Library {:A ".-":B "-...":C "-.-.":D "-..":E ".":F "..-.":G "--.":H "...."
:I "..":J ".---"
:K "-.-":L ".-..":M "--" :N "-.":O "---":P ".--.":Q "--.-":R ".-."
:S "...":T "-":U "..-":V "...-":W ".--":X "-..-":Y "-.--":Z "--.."
:0 "-----":1 ".----":2 "..---":3 "...--":4 "....-":5 "....."
:6 "-....":7 "--...":8 "---..":9 "----."})
(let [stringVector (str/upper-case(seq x))] ;divide the string into a sequence of characters
;trying to create iteration of the input where it checks if its value is found in the morse library
(doseq [[stringVector] (morse_Library)]
(if (= stringVector (morse_Library)
(do (println(str (key morse_Library))))
(do (println("characters not found"))))
)))
(print (str/upper-case stringVector))
)
(defn -main
[& args]
(println "ASCII to Morse Converter.")
(println "Make sure to include whitespaces after each ASCII character. Add String")
(def stringInput (read-line))
(println stringInput )
(morse->ASCI stringInput)
)
(-main)
I tried to create a "doseq" iteration where it checks if the value is found in the map.
Good things:
using a map for your morse translation; maps are also functions and
make it easy to write a transformation like this
Not so good things:
don't def inside other def:s - it's used to define namespace
global things and it is not like var or auto or whatever you
know from other languages
don't use keywords as map keys, if you don't plan to use them like
that; Clojure takes pretty much anything as key. So in your case
use chars
don't seq and then upper-case - wrong order - this will give you
the .toString() from a sequence of characters otherwise
don't name things all the same (stringVector)
doseq is for side-effects; you don't want your morse-code function
to do side-effects (you maybe want to print it later); in functional
programming you shift your side-effects to the edges - this way your
code becomes easier to test and reason about
you pretty much never need :gen-class
use the correct way to require in the
namespace
Clojure prefers snake-case over camelCase
(random rant: if you are using tutorialpoint (I assume this from several
things going wrong here and having them seen there) to learn Clojure: do
yourself a favour and look for another resource; as of now they don't
teach idiomatic Clojure!
Make your transformation a pure function:
upper-case the input
make a seq from it (this now is a sequence of chars) - but you don't
actually have to do this, because the next step will take care of it
map the look-up-table with your designated fallback over each char
join the result
This will look something like this:
(def morse {\A ".-" ,,, })
(->> input
(str/upper-case)
(map #(morse % "???"))
(str/join))
;; preparation of morse map
(ns morse
(:require [clojure.string :as str]))
;; I stole morse dictionary from some python code in:
;; https://www.geeksforgeeks.org/morse-code-translator-python/
(def s "{ 'A':'.-', 'B':'-...',
'C':'-.-.', 'D':'-..', 'E':'.',
'F':'..-.', 'G':'--.', 'H':'....',
'I':'..', 'J':'.---', 'K':'-.-',
'L':'.-..', 'M':'--', 'N':'-.',
'O':'---', 'P':'.--.', 'Q':'--.-',
'R':'.-.', 'S':'...', 'T':'-',
'U':'..-', 'V':'...-', 'W':'.--',
'X':'-..-', 'Y':'-.--', 'Z':'--..',
'1':'.----', '2':'..---', '3':'...--',
'4':'....-', '5':'.....', '6':'-....',
'7':'--...', '8':'---..', '9':'----.',
'0':'-----', ', ':'--..--', '.':'.-.-.-',
'?':'..--..', '/':'-..-.', '-':'-....-',
'(':'-.--.', ')':'-.--.-'}")
;; and transformed it using clojure to a clojure map:
(def m (read-string (str/replace
(str/replace
(str/replace
(str/replace s
"\n" "")
" " " ")
":" " ")
"'" "\"")))
;; now `m` contains the string-to-morse map
The actual answer starts here:
;; convert any text string to a morse string:
(defn string-to-morse [s]
(str/join " "
(map #(get m (str/upper-case %)) (str/split s #""))))
;; and this function can transform the output back to text:
(defn morse-to-string [morse-string]
(let [ms (zipmap (vals m) (keys m))]
(str/join (map #(get (zipmap (vals m) (keys m)) % " ")
(str/split morse-string #" ")))))
This question already has an answer here:
Wrong number of args (0) passed to: PersistentVector on loop/recur function
(1 answer)
Closed 5 years ago.
I've written a recursive function to get a total cost of a journey.
costOfPath simply makes a call to ubergraph to get the cost for each journey, then this function adds them and displays it.
(defn routeCost [parcel cost]
"Calculate the total route cost"
(if (empty? parcel)
(print "Total Journey Cost: " cost)
((def first-parcel (first parcel))
(def start (:start first-parcel))
(def finish (:finish first-parcel))
(def value (costOfPath start finish))
(def parcel-two (rest parcel))
(routeCost parcel-two (+ cost value)))))
(routeCost task8 0)
Task 8 looks as such:
(def task8 [(Parcel. :main-office :r131 "Plastic Wallets" "Delivery" 1)
(Parcel. :r131 :r111 "CDs" "Delivery" 1)
(Parcel. :r111 :r121 "USBs" "Collection" 2)
(Parcel. :r121 :main-office "USBs" "Delivery" 2)])
The function prints out the correct cost, but gives a classCastException.
ClassCastException practice_ubergraph.core.Parcel cannot be cast to clojure.lang.IFn clojure.lang.Var.fn (Var.java:363)
The parcel record:
(defrecord Parcel [start
finish
package
run-type
weight
])
Why is this happening and how can i stop it?
EDIT: I think its something to do with the IF statement and the way i've put the brackets around the block.
As Tony says, it's a good idea to try to limit your use of defs to the top level.
The reason you see a ClassCastException is probably this line:
((def first-parcel (first parcel))
You're defining first-parcel and then calling it immediately with the outer set of parentheseis.
Compare it to this example which generates a similar exception:
((def a 1))
In this example a gets the value 1. def returns the var #'user/a, so the expression that is evaluated is:
(#'user/a)
The value of #'user/a is 1, and 1 is then treated as a function.
Generally, if you see cannot be cast to clojure.lang.IFn look for a double set of brackets.
Please do NOT use def in a function.
Here is a much better one
(defn route-cost [parcel cost]
"Calculate the total route cost"
(if (empty? parcel)
(print "Total Journey Cost: " cost)
(let [{:keys [start finish]} (first parcel)
value (cost-of-path start finish)]
(route-cost (rest parcel) (+ cost value)))))
The essence of clojure is that you could write your code as concise as possible . usually we use kebab-case in clojure to differentiate Java
Use let in your function will fix everything
I've just started learning Clojure, after many years of Java (and PHP/JavaScript) experience. What a challenge :-)
How do I update a map of values idiomatically? When I use the map function on a map it doesn't return a map, it returns a sequence.
I'm working on a small app where I have a list of tasks. What I'd like to do is alter some of the values in some of the individual tasks, then update the list of original tasks. Here are the tasks I'm testing with:
(defrecord Task [key name duration])
(def tasks
(atom
{
"t1" (->Task "t1" "Task 1" 10)
"t2" (->Task "t2" "Task 2" 20)
"t3" (->Task "t3" "Task 3" 30)
}
))
I've put the tasks in a hashmap, using a string key so it has fast, direct access to any task in the map. Each task holds the key as well, so I know what it's key is when I'm passing individual tasks to other functions.
To update the durations I'm using map and update-in to iterate over and selectively update the duration of each task, and returning the modified tasks.
Here's the function:
(defn update-task-durations
"Update the duration of each task and return the updated tasks"
[tasks]
; 1) Why do I have to convert the result of the map function,
; from a sequence then back to a map?
(into {}
(map
(fn [task]
(println task) ; debug
(update-in
task
; 2) Why do I have to use vector index '1' here
; to get the value of the map entry?
[1 :duration]
(fn [duration]
(if (< duration 20)
(+ duration 1)
(+ duration 2)
)
)
)
) tasks))
)
I print the before/after values with this:
(println "ORIGINAL tasks:")
(println #tasks)
(swap! tasks update-task-durations)
(println "\nUPDATED tasks:")
(println #tasks)
1) The main problem I'm having is that the map function returns a sequence, and not a map, so I'm having to convert the sequence back to a map again using into {} which seems to me to be unnecessary and inefficient.
Is there a better way to do this? Should I be using a function other than map?
Could I arrange my data structures better, while still being efficient for direct access to individual tasks?
Is it ok to convert a (potentially very large) sequence to a map using into {} ?
2) Also, inside my function parameter, that I pass to the map function, each task is given to me, by map, as a vector of the form [key value] when I would expect a map entry, so to get the value from the map entry I have to pass the following keys to my update-in [1 :duration] This seems a bit ugly, is there a better/clearer way to access the map entry rather than using index 1 of the vector?
A popular way to solve this mapping-over-maps problem is with zipmap:
(defn map-vals
"Returns the map with f applied to each item."
[f m]
(zipmap (keys m)
(map f (vals m))))
(defn update-task-durations
[tasks]
(let [update-duration (fn [duration]
(if (< duration 20)
(+ 1 duration)
(+ 2 duration)))]
(->> tasks
(map-vals #(update % :duration update-duration)))))
(swap! tasks update-task-durations)
For Clojure < 1.7, use (update-in % [:duration] ... instead.
Alternatively, you could also use destructuring to simplify your current solution without defining a utility function:
(->> tasks
(map (fn [[k task]]
[k (update task :duration update-duration)]))
(into {})
Why?
map only deals with sequences. If you're into type signatures, this means that map always has the same type (map :: (a -> b) -> [a] -> [b]), but it also means that all you'll get out of map is a seq-of-something.
map calls seq on its collection parameter before doing anything, and seq-ing a map gives you a sequence of key-val pairs.
Don't worry too much about efficiency here. into is fast and this is pretty idiomatic.
Just get more alternatives:
Instead of a map you can use a for
(into {}
(for [[key value] your-map]
[key (do-stuff value)]))
A faster way is reduce-kv
(reduce-kv
(fn [new-map key value]
(assoc new-map key (do-stuff value)))
{}
your-map))
Of course you can also use a simple reduce
(reduce (fn [m key]
(update m key do-stuff))
your-map
(keys your-map))
This is my caller
(resolveEntity [r entity-id]
(println "resolve" entity-id)
(recursive-get r entity-id)
(cache entity-id)
)
Called function is
(defn recursive-get [r entity-id]
(println "recursive" entity-id)
(let [e (f (merge {} (-> r :conns first d/db (d/entity entity-id))))]
(alter-var-root #'cache assoc entity-id e)
(for [[k v] e]
(if (:db/isComponent (k components))
(if (not= (class v) Long)
(map #(recursive-get r %) v)
(recursive-get r v)
)))))
The called function is called just once. If I remove the last line in the caller (cache entity-id), then it recurs every time that I want it to, but I need to return something else (cache entity-id).
I tested a similar but simpler code (a recursive function not called at the tail of a calling function) the REPL and it worked, so I am left crashing my head against the table.
You have been bitten by a Lazy-Bug!
If you remove the last line then the return value of the function is the result of (recursive-get r entity-id) which the repl then iterates through so it can print it. The act of printing each value causes each entry in the lazy collection to be evaluated. When you put another line after that, the result of the map is ignored: nothing reads the entries, and they remain in the unrealized lazy state forever and the computation never happens.
To fix this wrap it in a call to dorun:
(dorun (recursive-get r entity-id))
Or if you need to save the result then use doall instead.
I am trying to get a handle on the new defprotocol, reify, etc.
I have a org.w3c.dom.NodeList returned from an XPath call and I would like to "convert" it to an ISeq.
In Scala, I implemented an implicit conversion method:
implicit def nodeList2Traversable(nodeList: NodeList): Traversable[Node] = {
new Traversable[Node] {
def foreach[A](process: (Node) => A) {
for (index <- 0 until nodeList.getLength) {
process(nodeList.item(index))
}
}
}
}
NodeList includes methods int getLength() and Node item(int index).
How do I do the equivalent in Clojure? I expect that I will need to use defprotocol. What functions do I need to define to create a seq?
If I do a simple, naive, conversion to a list using loop and recur, I will end up with a non-lazy structure.
Most of Clojure's sequence-processing functions return lazy seqs, include the map and range functions:
(defn node-list-seq [^org.w3c.dom.NodeList node-list]
(map (fn [index] (.item node-list index))
(range (.getLength node-list))))
Note the type hint for NodeList above isn't necessary, but improves performance.
Now you can use that function like so:
(map #(.getLocalName %) (node-list-seq your-node-list))
Use a for comprehension, these yield lazy sequences.
Here's the code for you. I've taken the time to make it runnable on the command line; you only need to replace the name of the parsed XML file.
Caveat 1: avoid def-ing your variables. Use local variables instead.
Caveat 2: this is the Java API for XML, so there objects are mutable; since you have a lazy sequence, if any changes happen to the mutable DOM tree while you're iterating, you might have unpleasant race changes.
Caveat 3: even though this is a lazy structure, the whole DOM tree is already in memory anyway (I'm not really sure about this last comment, though. I think the API tries to defer reading the tree in memory until needed, but, no guarantees). So if you run into trouble with big XML documents, try to avoid the DOM approach.
(require ['clojure.java.io :as 'io])
(import [javax.xml.parsers DocumentBuilderFactory])
(import [org.xml.sax InputSource])
(def dbf (DocumentBuilderFactory/newInstance))
(doto dbf
(.setValidating false)
(.setNamespaceAware true)
(.setIgnoringElementContentWhitespace true))
(def builder (.newDocumentBuilder dbf))
(def doc (.parse builder (InputSource. (io/reader "C:/workspace/myproject/pom.xml"))))
(defn lazy-child-list [element]
(let [nodelist (.getChildNodes element)
len (.getLength nodelist)]
(for [i (range len)]
(.item nodelist i))))
;; To print the children of an element
(-> doc
(.getDocumentElement)
(lazy-child-list)
(println))
;; Prints clojure.lang.LazySeq
(-> doc
(.getDocumentElement)
(lazy-child-list)
(class)
(println))