In Clojure I want to create a function that would continually ask the user for an option, execute some code depending on the option, and then quit when the user’s option is “q”.
I am comfortable will all of the various Clojure forms that work with sequences, and I could certainly finagle a Java-like solution to the above, but I cannot figure out how to do this in a “clojuresque” manner.
Thanks,
Jeffrey S
Something like this should do the job:
(defn main-loop []
(case (read-line)
"q" nil
"a" (do (println "got a command!") (recur))
"b" (do (println "got b command!") (recur))
(do (println "got invalid command!") (recur))))
Related
"Clojure metaphysics construes identity as something we humans impose on a succession of unchanging values produced by a process over time".
(Higginsworth, October 2015)
If this is true, if identity surronds all these states, then I should be able to do something like this.
user=> (def wow (atom 1))
#'user/wow
user=> (swap! wow (fn [cur] "You say 'Hello'."))
"You say 'Hello'."
user=> (swap! wow (fn [cur] "I say 'Goodbye'."))
"I say 'Goodbye'."
user=> (swap! wow (fn [cur] "Hello, hello!"))
"Hello, hello!"
; Can I do this?
user=> (get-old-atom-state wow 0)
1
(get-old-atom-state wow 1)
"You say 'Hello'"
Is this so? Or does Clojure actually GC the old values if not used?
If this is true, if identity surronds all these states, then I should be able to do something like this.
I don't really see any justification for this "should", philosophically. "Aaron Bell" is an identity that encompasses many states you have been in in the past, but I can't interact with, or even observe, any of those past states. The only way I would know about any of them is if somebody had observed them and written down those observations in some immutable object that I can refer to.
Clojure's identities behave the same way: you can take a snapshot at any time, and after you do, you can look at that snapshot whenever you want. But states nobody looked at are lost forever.
The JVM does garbage collect the old and not referenced values of an atom.
However, you can set up a listener to the changes of the atom values and store the old values manually to another atom.
user=> (def wow (atom 1))
#'user/wow
user=> (def wow-history (atom ()))
#'user/wow-history
user=> (add-watch wow :hist
(fn [_ _ old _] (swap! wow-history conj old)))
Mutate the atom:
user=> (swap! wow (fn [cur] "You say 'Hello'."))
"You say 'Hello'."
user=> (swap! wow (fn [cur] "I say 'Goodbye'."))
"I say 'Goodbye'."
user=> (swap! wow (fn [cur] "Hello, hello!"))
"Hello, hello!"
Now lets check the values in the history:
user=> #wow-history
("I say 'Goodbye'." "You say 'Hello'." 1)
Do not forget to clean up the wow-history atom when you do not need the old values to prevent memory leaks.
user=> (reset! wow-history ())
()
I've been experimenting with creating monads in clojure so I can separate my pure code from my impure code more thouroghly, and I've stumbled upon something strage with ordering.
When using the following code for a minimum use-case, the desired output is for it to print "Please enter your name: " to the screen and then take a single line of input, which is then printed back out to the screen. The code below is what should be able to acccomplish this:
;; IO Monad definitions
(defn io-bind
[mv mf]
(fn []
(let [val (mv)
f (mf val)]
(f))))
;; Monadic functions
(defn io-print
[msg]
(fn []
(print msg)))
(defn io-read-line
[_]
(fn []
(read-line)))
;; Entry point
(defn -main
"I don't do a whole lot ... yet."
[& args]
((-> (io-print "Please enter your name: ")
(io-bind io-read-line)
(io-bind io-print))))
I've experienced a hitch with this however, in that it will first poll for input from the user using (read-line) and only afterwards will it then print "Please enter your name: ".
In larger examples, it will still perform all of the visible IO actions in the proper order, for example in this case it does print out "Please enter your name: " before it prints out what you inputted, yet it still requests that input first.
The strangest part however is that the instant I replace print with println in io-print it does all of the ordering as intented, regardless of circumstance.
Why might this be occuring? Is there some way that print is lazy that println is not, for example?
You need to add the following after the print:
(flush)
println will implicitly flush the output to the screen, so that is why you are seeing different behavior than with print.
Please see https://clojuredocs.org/clojure.core/flush for more info, as also the source code for all the details.
I have a function that looks like,
(defn app [server]
(println "before while...."))
(while test
while-body)
(println "...after while."))
However when I call the fn I just see the "before while" at the REPL, and then when the while fails its test, "nil".
If I write a test foo at the repl like
(defn foo []
(println "testing before")
(loop [i 100]
(when (> i 10)
(prn i)
(recur (- i 2))))
(println "after..."))
It works as I'd expect.
I've put the actual code up in a paste here, https://www.refheap.com/paste/12147 , if it helps.
What explains the difference in behavior here?
edit
Apologies for not trying this before, but this does work at the REPL:
(defn bar []
(let [i (atom 100)]
(println "before...")
(while (> #i 10)
(swap! i dec))
(println "after...")))
So there's something else going on.
edit #2
Testing more at the repl, if I comment out the while loop, the println before and after will print. I was mistaken before about the 'nil', this is the return value of a different function called after the while was called. So it seems to have something to do with the while loop.
I noticed that if I change the while to this
(loop []
(if test
(do things and recur...)
(println "test failed")))
The "test failed" never prints to the repl.
You've got an extranious ) at the end of the first println.
(defn app [server]
(println "before while....")
(while test
while-body)
(println "...after while."))
But since this is obviously example code that you didn't run, I expect the problem to be in the code that you did run. Please copy & paste that code exactly as is if this doesn't fix the problem.
The problem wasn't what I thought it was. I was blocking on a select call (called in the while loop) and that was causing problems with my shutdown function which ended the while loop. Adding a timeout to the select fixes it.
In Clojure function definitions, the last thing to be evaluated is what's returned, and (println ...) returns nil
I have an incoming lazy stream lines from a file I'm reading with tail-seq (to contrib - now!) and I want to process those lines one after one with several "listener-functions" that takes action depending on re-seq-hits (or other things) in the lines.
I tried the following:
(defn info-listener [logstr]
(if (re-seq #"INFO" logstr) (println "Got an INFO-statement")))
(defn debug-listener [logstr]
(if (re-seq #"DEBUG" logstr) (println "Got a DEBUG-statement")))
(doseq [line (tail-seq "/var/log/any/java.log")]
(do (info-listener logstr)
(debug-listener logstr)))
and it works as expected. However, there is a LOT of code-duplication and other sins in the code, and it's boring to update the code.
One important step seems to be to apply many functions to one argument, ie
(listen-line line '(info-listener debug-listener))
and use that instead of the boring and error prone do-statement.
I've tried the following seemingly clever approach:
(defn listen-line [logstr listener-collection]
(map #(% logstr) listener-collection))
but this only renders
(nil) (nil)
there is lazyiness or first class functions biting me for sure, but where do I put the apply?
I'm also open to a radically different approach to the problem, but this seems to be a quite sane way to start with. Macros/multi methods seems to be overkill/wrong for now.
Making a single function out of a group of functions to be called with the same argument can be done with the core function juxt:
=>(def juxted-fn (juxt identity str (partial / 100)))
=>(juxted-fn 50)
[50 "50" 2]
Combining juxt with partial can be very useful:
(defn listener [re message logstr]
(if (re-seq re logstr) (println message)))
(def juxted-listener
(apply juxt (map (fn [[re message]] (partial listner re message))
[[#"INFO","Got INFO"],
[#"DEBUG", "Got DEBUG"]]))
(doseq [logstr ["INFO statement", "OTHER statement", "DEBUG statement"]]
(juxted-listener logstr))
You need to change
(listen-line line '(info-listener debug-listener))
to
(listen-line line [info-listener debug-listener])
In the first version, listen-line ends up using the symbols info-listener and debug-listener themselves as functions because of the quoting. Symbols implement clojure.lang.IFn (the interface behind Clojure function invocation) like keywords do, i.e. they look themselves up in a map-like argument (actually a clojure.lang.ILookup) and return nil if applied to something which is not a map.
Also note that you need to wrap the body of listen-line in dorun to ensure it actually gets executed (as map returns a lazy sequence). Better yet, switch to doseq:
(defn listen-line [logstr listener-collection]
(doseq [listener listener-collection]
(listener logstr)))
I'm currently learning clojure, but I was wondering how to get and store user input in a clojure program. I was looking at the clojure api and I found a function called read-line, however I'm not sure how to use it if it's the right function to use...
Anyhow, how do you get user input in clojure ?
read-line is the correct function..
(println (read-line))
..would basically echo the users input:
Clojure 1.0.0-
user=> (println (read-line))
this is my input
this is my input
To use it in an if statement, you'd probably use let:
(let [yayinput (read-line)]
(if (= yayinput "1234")
(println "Correct")
(println "Wrong")))
Hope that's enough to get you started, because that's about the limit of my Clojure knowledge!
Remember also that you have access to all of Java ...
OK so perhaps I should present some examples ... my clojure skills are not good so these examples may need a bit of tweaking.
The System.console() way:
(let [console (. System console)
pwd (.readPassword console "tell me your password: ")]
(println "your password is " pwd))
The BufferedReader way:
(print "give me a line: ")
(let [reader (java.io.BufferedReader. *in*)
ln (.readLine reader)]
(println "your line is " ln))
My point is that one can leverage knowledge of Java, and Java itself, in Clojure. It's one of its principal, advertised strengths.
Wonder what would my score have been if the question were about user input from a GUI!
By the way, you could use JOptionPane to put up a little GUI to get user input ...
read-line is used to get user input and use let to bind it to some variable.
For example : if you want to read user ID and password from user and display it, you could use the following piece of code
(defn print-message [pid pw]
(println "PID : " pid)
(println "PW : " pw))
(defn inp[]
(println "Enter your PID and password")
(let[pid (read-line) pw (read-line)]
(print-message pid pw) ))