"Update can not be used in this context" - file-io

I am new to clojure and I am working on a problem...
I'm am trying to find the frequency of all the instructors I have in a file, but I have no idea where to start. I did this before in a different program and it worked, but now i am getting an error that reads "Update can not be used in this context"
(defn read-lines [filename]
(with-open [rdr (clojure.java.io/reader filename)]
(doall (line-seq rdr))))
(defn classes [s]
(reduce conj (map hash-map [:semester :title :crn :code :levels :credits
:campus :section :capacity :actual :starthout :startmin
:endhour :endmin :weekday :room :datestart
:dateend :schtypye :instructor :prereq :corereq] (.split s ";"))))
(println(map classes (read-lines "C:/Users/Rohil's Computer/Desktop/textfile.txt")))
(loop [semester->instructor {}
[{:keys [semester instructor] :as row} & rows] classes]
(if (nil? row)
semester->instructor
(recur (update semester->instructor semester (fnil conj []) instructor) rows)))

Since you really didn't ask a specific question I'm going to extrapolate given what you said and the code that you posted.
The real question is "Where do I start?"
In Clojure that answer is always "at the repl"
so first you defined read-lines, which seems to take a file name and returns a collection with each item in the collection being the line of the file passed in
That's simple enough and easy to test at the repl.
but then you have the classes function which seems to take a semi-colon delimited string and tries to split it at the semi colon to make a map of the hard-coded keys to values in the string.
After that it kind of goes off the rails.
So lets pick up before that and look at a simpler example; follow along at the repl
(defn classes [s]
(reduce conj (map hash-map [:instructor :semester]
(.split s ";"))))
(map classes ["bob;1" "jack;2" "bob;3"]) returns a collection of maps. in this case it returns ({:semester "1", :instructor "bob"}
{:semester "2", :instructor "jack"}
{:semester "3", :instructor "bob"})
we want to get the frequencies of all names so lets pull those out into a simple list by mapping the instructor keyword over this collection of maps like so:
(map :instructor (map classes ["bob;1" "jack;2" "bob;3"])) so we get
("bob" "jack" "bob")
now we can just call frequencies on that collection to get a map of name to frequency like this:
(frequencies (map :instructor (map classes ["bob;1" "jack;2" "bob;3"])))
this returns {"jack" 1, "bob" 2}
Now that each of those individual pieces seems to be working at the repl we can put them all together:
(->> (read-lines "C:/Users/Rohil's Computer/Desktop/textfile.txt")
(map classes)
(map :instructor)
frequencies)

Related

The relative clause (which) in GF

"Play Toy Story which was published last year"
Sentence = mkUtt( mkImp (mkVP
(mkV2 "play")
(mkNP (mkCN
(mkCN (mkN "Toy Story"))
(mkS pastTense simultaneousAnt(mkCl (mkVP
(mkV2 "publish")
(mkNP (mkCN (mkN "last yser")))
)))
))
));
When creating a relative clause sentence in GF it always the syntax S for sentence will add that between the two of the sentences is there is any way to replace that with which.
First of all, the structure you made isn't a relative clause, but this structure:
mkCN : CN -> S -> CN -- rule that she sleeps
Relative clause has the type RS in the RGL.
How to construct an actual RS
Here I build the RS gradually. Feel free to put these steps back to a single expression, if you wish so, but I find it clearer to to things like this.
oper
last_year_Adv : Adv = ParadigmsEng.mkAdv "last year" ;
published_last_year_VP : VP = mkVP (passiveVP (mkV2 "publish")) last_year_Adv ;
which_is_published_last_year_RCl : RCl = mkRCl which_RP published_last_year_VP ;
which_was_published_last_year_RS : RS = mkRS pastTense which_is_published_last_year_RCl ;
lin
play_TS = mkUtt (mkImp (mkVP
(mkV2 "play")
(mkNP
(mkNP (mkPN "Toy Story"))
which_was_published_last_year_RS)
)
) ;
Now when we test it on the GF shell, we see that despite the name, which_RP is actually "that".
> l play_TS
play Toy Story , that was published last year
How to change "that" into "which"
The first thing to check when you want to create a new lexical item is Paradigms module. Unfortunately, there is no mkRP for English. Things like relative pronouns are usually thought of as closed class: there's only a small, fixed amount of them. Other examples of closed classes are basic numerals (you can't just make up a new integer between 4 and 5!) and determiners. Contrast this to open classes like nouns, verbs and adjectives, those pop up all the time. For open classes, Paradigms modules have many options. But not for closed classes, like relative pronouns.
So if Paradigms doesn't help, the next thing to check is the language-specific Extra module.
Check language-specific Extra modules
If you have the RGL source on your own computer, you can just go to the directory gf-rgl/src/english and grep for which. Or you can use the RGL browser to search for interesting functions.
And there is indeed a relative pronoun in ExtraEng, also called which_RP. (There is also one called who_which_RP.) So now you can do these modifications in your grammar:
concrete MyGrammarEng of MyGrammar =
open SyntaxEng,
ParadigmsEng,
ExtraEng -- Need to open ExtraEng in the concrete!
in ** {
-- … as usual, except for
oper
which_is_published_last_year_RCl : RCl =
mkRCl ExtraEng.which_RP -- Use the ExtraEng.which_RP!
published_last_year_VP ;
And the rest of the code is like before. This produces the result you want.
> l play_TS
play Toy Story , which was published last year
Last-resort hack
So you have looked in all possible modules and found nothing. Consider making it into an issue in the gf-rgl repository, if it's something that's clearly wrong or missing.
But in any case, here's a general, unsafe hack to quickly construct what you want.
First, let's look at the lincat of RP in CatEng, {s : RCase => Str ; a : RAgr}. Then let's look at its implementation in RelativeEng. There you see also the explanation why it's always "that": unlike who and which, "that" works for animate and inanimate NPs.
So I would do this to force the string "which":
oper
myWhich_RP : RP = which_RP ** {s = table {_ => "which"}} ;
The ** syntax means record extension. We use all other fields from the original which_RP, but in the s field, we put a table whose all branches contain the string "which". (You can read more about this technique in my blog.)
Then we use the newly defined myWhich_RP in forming the relative clause:
oper
which_is_published_last_year_RCl : RCl = mkRCl myWhich_RP published_last_year_VP ;
This works too, but it's unsafe, because whenever the RGL changes, any code that touches the raw implementation may break.

Count the frequency of a string in map in clojure

I am fairly new to clojure and just practicing some exam questions to prepare for a final exam.
I am trying to find the frequency of all the names is in a file. I read the file line by line and I save each string if it contains a specific keyword in a map. Since I do not want any repetitions, I am trying put distinct out in front of it, but I still keep getting the repeating elements.
(defn readFile []
(map (fn [line] (clojure.string/split line #";"))
(with-open [rdr (reader "C:/Users/Rohil's Computer/Desktop/textfile.txt")]
(doseq [[idx line] (map-indexed vector(line-seq rdr))]
(if(.contains line "2007")
(if(.contains line "May")
(if(not(.contains line "Batman"))
(map save [(nth(clojure.string/split line #";")3 (nth(clojure.string/split line #";")19)])
(distinct(map))
)
)
)
)
)
)
)
Here is my sample output:
I want to get rid of the 2 iron man elements.
(May 2007 Spiderman)
Clojure2.clj:
(March 2007 Iron man)
Clojure2.clj:
(March 2007 Iron man)
Clojure2.clj:
(April 2007 Captain America)
Is there anything I am missing?
With Clojure a good approach is to break down what you need to accomplish a task into functions that are as simple as possible. Once you have written and tested those simple functions you will be able to combine them together. Often times, the simple function you need already exists, as is here the case with frequencies:
(frequencies ["foo" "bar" "foo" "quux" "foo"])
=> {"foo" 3, "bar" 1, "quux" 1}
So it sounds like all you need to do really is tokenize the input file and apply frequencies to the list of tokens.

Functional way to make multiple API requests with Clojure

I'm working on a Clojure application that will interact with a web API to return a random result meeting a specific criterion. Because of the limitations of the API, I have to retrieve a number of results and use Clojure to filter out an appropriate one.
I would say 99% or more of the time, at least one of the results from the first request will meet my criterion. But because of that other 1%, I feel I have to build in functionality to make multiple requests. Obviously the odds are pretty good that the second request will be successful but I'm leery about writing a function that recurs endlessly until it gets the right result in case something goes wrong and I end up DDoSing the API.
My first thought was to build a counter into the function and limit it to, say, five requests. But that feels a bit procedural as an approach. What's the idiomatic Clojure way to go about this?
Here's what I have so far:
(ns randchar.api
(:require [clj-http.client :as client]))
(defn api-request
[url]
(get-in
(client/request
{:url url
:method :get
:content-type :json
:as :json}) [:body :results]))
(defn filter-results
"Returns the first result that meets the criterion."
[results criterion]
(take 1
(filter #(= (get-in % [:nested :sub-nested]) criterion) results)))
(defn build-url
"Returns a url with randomized query string for the api request; not shown."
[]
)
(defn return-result
"Currently recurs endlessly if no results meet the criterion."
[criterion]
(let [x (filter-results (api-request (build-url)) criterion)]
(if (not (empty? x))
x
(recur))))
You can try something like:
(defn return-result
[criterion count]
(->> (repeatedly count #(filter-results (api-request build-url) criterion))
(filter (complement empty?))
first))

Clojure defrecord serialization ClassNotFoundException

I was trying to serialise one of my records to a human readable format. While serialising using Java serialiser worked fine I am trying to use print-dup. The problem I am facing is that while writing the record goes fine reading the record results in clojure.lang.LispReader$ReaderException: java.lang.ClassNotFoundException: common.dummy.Doodh. Am I messing up the namespaces or something?
Please note that this is not a problem with Java serialisation.
Code below in the simplest form
(ns common.dummy)
(defrecord Doodh [id name])
(defn output [filename obj]
(def trr(map->Doodh {:id "moooh" :name "Cows"}))
(def my-string (binding [*print-dup* true] (pr-str trr)))
(spit filename my-string)
)
(defn pull [filename]
(def my-data (with-in-str (slurp filename) (read)))
(println my-data)
)
text file contents:
#common.dummy.Doodh["moooh", "Cows"]
Don't use def inside function definitions. When you are using def, you create a var in your namespace and possibly manipulate it as a side-effect with every function call. Use let-blocks.
If you want to save Clojure data structures in a file, use clojure.edn. It is safe (e. g. without your knowledge, no functions defined in the file will be invoked) but it allows to enable custom readers (see more below).
A type defined with defrecord can be printed in a (Clojure-reader-)readable way using pr-str (thanks to #A. Webb for noting that). In your example, I don't see why you would not stick to a hash-map in the first place, but if you really need a defrecord here, you may convert it into a readable string before your write it to the file.
(defrecord Doodh [id name])
(defn output [filename obj]
(spit filename (pr-str obj))
(defn pull [filename]
(with-in-str (slurp filename)
(read)))
This way of doing it has several disadvantages.
Using read makes your code vulnerable to function calls in the slurped files (like #=(java.lang.System/exit 0)).
An exception will be thrown when the file at filename is empty.
Finally your saved file will become incompatible to your code when you move your defrecord declaration to another namespace.
All three disadvantages can be avoided by using the edn-reader.
Using a custom reader with EDN
We extend our type Doodh by implementing the toString method of the java.lang.Object interface:
(defrecord Doodh [id name]
Object
(toString [this] (str "#Doodh" (into {} this))))
Because spit uses str, we can now omit the output function and simply invoke spit from e. g. the REPL:
(spit "Doodh.edn" (map->Doodh {:id "134" :name "Berta"}))
Doodh.edn:
#Doodh{:id 134, :name "Berta"}
Now to make sure that the Doodh will be read back, we invoke clojure.edn/read-string with a custom reader function:
(defn pull [filename]
(->> (slurp filename)
(clojure.edn/read-string {:readers {'Doodh map->Doodh}})))
If you read back "Doodh.edn" using the new pull, you should receive a valid Doodh. At the REPL:
(pull "Doodh.edn")
=> #user.Doodh{:id 134, :name "Berta"}

Does clooj evaluates line by line or by the whole line

It seems when i evaluate the whole file it does not have an issue.
(ns ShipDataRecord
(:import [java.util.Date]
[org.joda.time.DateTime]
[org.joda.time.Seconds]
[org.joda.time.format.*]
[semsav.RecordSplitter]))
(require '[clojure.data.csv :as csv]
'[clojure.java.io :as io])
(defrecord Record [W1 W2 W3])
(defn read-csv [fname count]
(with-open [file (reader fname)]
(doall (take count (map (comp first csv/read-csv)
(line-seq file))))))
(map #(apply ->Record %) (read-csv "test.csv" 1))
However, when i evaluate line by line it seems to give me a problem.
Hence after evaluating the file, i ran this line in the REPL
(:W3 (first Record))
but it gives me a compiler exception of IllegalArgumentException Don't know how to create ISeq from: java.lang.Class clojure.lang.RT.seqFrom (RT.java:494).
I have googled around but i cant seem to find the problem. I have asked this in another question but as the words are too long i have to create a new question
In (:W3 (first Record)) the symbol Record represents class Record. I guess from your previous post you just want to get :W3 field from first record of collection of records produced by (map #...). So all you need is to get this value right from map expression or from variable where you can store result of expression:
(:W3 (first (map #(apply ->Record %) (read-csv "1.csv" 1))))
or
(def records (map #(apply ->Record %) (read-csv "1.csv" 1)))
(:W3 (first records))