I am working on a problem to read in a file with lines like:
A abcdefg
B bcdefgh
But I keep getting errors about Lazy Sequence not compatible with Java Charseq ..
I tried:
(def notlazy (doall lyne2))
Then thought I verified:
(realized? notlazy)
true
But still:
(str/split notlazy #" ")
ClassCastException class clojure.lang.LazySeq cannot be cast to class
java.lang.CharSequence (clojure.lang.LazySeq is in unnamed module of
loader 'app'; java.lang.CharSequence is in module java.base of loader
'bootstrap') clojure.string/split (string.clj:219)
Help please!
The first argument to str/split must be a CharSequence to be split. Presumably you want to split each input line in the sequence for which you can use map without needing to eagerly evaluate the input sequence:
(map (fn [line] (str/split line #" ")) lyne2)
Expanding on the previous result a bit, we have this example. You can reproduce the following using this template project.
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.java.io :as io]
[tupelo.string :as str]
))
(def data-file
"A abcdefg
B bcdefgh
")
(dotest
; Version #1
(let [lines (line-seq (io/reader (str/string->stream data-file)))
lines2 (remove str/blank? lines)
lines3 (map str/trim lines2)
line-words (mapv #(str/split % #"\s+") lines3) ; "\s+" => "one or more whitespace chars"
]
(spyxx lines)
(spyxx lines2)
(spyxx lines3)
(spyxx line-words))
with result:
--------------------------------------
Clojure 1.10.2-alpha1 Java 15
--------------------------------------
Testing tst.demo.core
lines => <#clojure.lang.Cons ("A abcdefg" " B bcdefgh" " ")>
lines2 => <#clojure.lang.LazySeq ("A abcdefg" " B bcdefgh")>
lines3 => <#clojure.lang.LazySeq ("A abcdefg" "B bcdefgh")>
line-words => <#clojure.lang.PersistentVector [["A" "abcdefg"] ["B" "bcdefgh"]]>
This shows the type of each result along with its value. We use string->stream so we don't need to set up a dummy file to read from.
The following shows how it would typically be written in real code (not as a demo exercise like Version #1). We use the "thread-last" operator, and write a unit test to verify the result:
; Version #2
(let [result (->> data-file
(str/string->stream)
(io/reader)
(line-seq)
(remove str/blank?)
(map str/trim)
(mapv #(str/split % #"\s+"))) ; "\s+" => "one or more whitespace chars"
]
(is= result [["A" "abcdefg"] ["B" "bcdefgh"]])))
I'm writing a function to parse out IRC RFC2813 messages into their constituent parts. This consists of two functions, one to split the message via regex, and another to modify the return to handle certain special cases.
(let [test-privmsg ":m#m.net PRIVMSG #mychannel :Hiya, buddy."])
(defn ircMessageToMap [arg]
"Convert an IRC message to a map based on a regex"
(println (str "IRCMapifying " arg))
(zipmap [:raw :prefix :type :destination :message]
(re-matches #"^(?:[:](\S+) )?(\S+)(?: (?!:)(.+?))?(?: [:](.+))?$"
arg
)
)
)
(defn stringToIRCMessage [arg]
"Parses a string as an IRC protocol message, returning a map"
(let [r (doall (ircMesgToMap arg))])
(println (str "Back from the wizard with " r))
(cond
;Reformat PING messages to work around regex shortcomings
(= (get r :prefix) "PING") (do
(assoc r :type (get r :prefix))
(assoc r :prefix nil)
)
;Other special cases here
:else r)
)
The problem I'm running into is that the stringToIRCMessage function doesn't appear to be realizing the return value of ircMesgToMap. If I evaluate (stringToIRCMessage test-privmsg), the println statement gives me:
Back from the wizard with Unbound: #'irc1.core/r
..but the "IRCMapifying" result from ircMessageToMap appears on the console beforehand indicating that it was evaluated correctly.
The doall was an attempt to force the result to be realized in the middle of the function - it had no effect.
How should I rewrite this stringToIRCMessage function to get the r variable usable?
The parens are wrong in your let statement.
Should look like this:
(let [r (doall (ircMesgToMap arg)) ]
(println (str "Back from the wizard with " r))
(cond
;Reformat PING messages to work around regex shortcomings
(= (get r :prefix) "PING") (do
(assoc r :type (get r :prefix))
(assoc r :prefix nil)
)
;Other special cases here
:else r))
Input: "Michael" "Julia" "Joe" "Sam"
Output: Hi, Michael, Julia, Joe, and Sam. (pay attention to the commas and the word "and")
Input: nil
Output: Hi, world.
Here is my first attempt:
(defn say-hi [& name]
(print "Hi," name))
user> (say-hi "Michael")
Hi, (Michael)
nil
user> (say-hi "Michael" "Julia")
Hi, (Michael Julia)
nil
Question:
How to implement default: (no input, say "Hi World!")
How to get rid of the parents around names in output?
How to implement the commas separation and add the conjunction word "and"?
First off, Clojure supports multi-arity functions, so you could do something like this to achieve default behaviour:
(defn say-hi
([] (say-hi "World"))
([& names] ...))
Then, what you want is to take a seq and join all the strings it contains together, using ", " in between. The clojure.string namespaces contains lots of string manipulation functions, one of them being clojure.string/join:
(require '[clojure.string :as string])
(string/join ", " ["Michael", "Julia"])
;; => "Michael, Julia"
But the last element of the seq should be concatenated using " and " as a separator, so you'll end up with something like this:
(require '[clojure.string :as string])
(defn say-hi
([] (say-hi "World"))
([& names]
(if (next names)
(format "Hi, %s, and %s!"
(string/join ", " (butlast names))
(last names))
(format "Hi, %s!" (first names)))))
Note that you have to differentiate between the single- and multi-name cases and (next names) basically checks whether the seq contains more than one element. (You could achieve the same by adding another arity to the function.)
(say-hi)
;; => "Hi, World!"
(say-hi "Michael")
;; => "Hi, Michael!"
(say-hi "Michael" "Julia" "Joe" "Sam")
;; => "Hi, Michael, Julia, Joe, and Sam!"
You can use clojure.string/join:
(use '[clojure.string :only [join]])
(defn sentencify [& elems]
(->>
[(join ", " (butlast elems)) (last elems)]
(remove empty?)
(join " and ")))
(defn say-hi [& name]
(print "Hi," (if name
(sentencify name)
"World!")))
A concise solution:
(defn say-hi [& names]
(let [names (case (count names)
0 ["world"]
1 names
(concat (butlast names) (list (str "and " (last names)))))]
(->> names, (cons "Hi"), (interpose ", "), (apply str))))
(say-hi)
;"Hi, world"
(say-hi "Michael")
;"Hi, Michael"
(say-hi "Michael" "Julia" "Joe" "Sam")
;"Hi, Michael, Julia, Joe, and Sam"
For long lists of names, you would want to eschew count, last, and butlast, maybe by pouring names into a vector first.
To print (as the question does) rather than return the formatted string, append print to the final form:
(->> names, (cons "Hi"), (interpose ", "), (apply str), print)
I'm trying to validate the protocol of an instance of defrecord that I generate dynamically using clojure.core/extend
Below You can see that satisfies returns true and (s/validate (s/protocol ...)) doesn't throw exception, but if I run s/with-fn-validation I get a "(not (satisfies? protocol ..... " schema exception although inside this body I keep getting the same true result for (satisfies? protocol x)
(ns wrapper.issue
(:require [schema.core :as s]))
(defprotocol Welcome
(greetings [e] )
(say_bye [e a b])
)
(s/defn greetings+ :- s/Str
[component :- (s/protocol Welcome)]
(greetings component))
(defrecord Example []
Welcome
(greetings [this] "my example greeting!")
(say_bye [this a b] (str "saying good bye from " a " to " b))
)
(defrecord MoreSimpleWrapper [e])
(extend MoreSimpleWrapper
Welcome
{:greetings (fn [this]
(str "wrapping!! " (greetings (:e this)))
)
:say_bye (fn [this a b]
(str "good bye !"))})
(println (satisfies? Welcome (MoreSimpleWrapper. (Example.))))
;;=>true
(println (s/validate (s/protocol Welcome) (MoreSimpleWrapper. (Example.))))
;;=>#wrapper.issue.MoreSimpleWrapper{:e #wrapper.issue.Example{}}
(s/with-fn-validation
(println (satisfies? Welcome (MoreSimpleWrapper. (Example.))))
;;=>true
(println (s/validate (s/protocol Welcome) (MoreSimpleWrapper. (Example.))))
;;=>#wrapper.issue.MoreSimpleWrapper{:e #wrapper.issue.Example{}}
)
(s/with-fn-validation
(greetings+ (MoreSimpleWrapper. (Example.))))
;;=>CompilerException clojure.lang.ExceptionInfo: Input to greetings+ does not match schema: [(named (not (satisfies? Welcome a-wrapper.issue.MoreSimpleWrapper)) component)] {:schema [#schema.core.One{:schema (protocol Welcome), :optional? false, :name component}], :value [#wrapper.issue.MoreSimpleWrapper{:e #wrapper.issue.Example{}}], :error [(named (not (satisfies? Welcome a-wrapper.issue.MoreSimpleWrapper)) component)]}, compiling:(/Users/tangrammer/git/olney/wrapper/src/wrapper/issue.clj:39:69)
Here also a gist with the code
It was a schema bug :)
now fixed in snapshot-0.3.2
https://github.com/Prismatic/schema/issues/164
this program opens a file read it into a list
then asks the user to enter the word from the list one at a time
but I get an error right after it speaks
(ns EKS.Core)
(use '[speech-synthesis.say :as say])
(use '[clojure.java.shell :only [sh]])
(use 'seesaw.core)
(use 'seesaw.dev)
(use 'seesaw.chooser)
(use 'clojure.java.io)
(import 'java.io.File)
(native!)
(defn festival [x](sh "sh" "-c" (str "echo " x " | festival --tts")))
(defn espeak [x] (sh "espeak" x))
(defn mac-say[x] (sh "say" x))
(defn check-if-installed[x] (:exit(sh "sh" "-c" (str "command -v " x " >/dev/null 2>&1 || { echo >&2 \"\"; exit 1; }"))))
(defn env
"Returns the system property for user.<key>"
[key]
(System/getProperty (str "user." key)))
(defn get-lines [fname]
(with-open [r (reader fname)]
(doall (line-seq r))))
(defn engine-check[]
(def engines (conj["Google" ]
(if (= (check-if-installed "festival") 0) "Festival" )
(if (= (check-if-installed "espeak") 0) "ESpeak" )
(if (= (check-if-installed "say") 0) "Say" )))(remove nil? engines))
(engine-check)
(def ListDir (str (env "home") ))
(def speak say)
(defn setup-list[ file](
(def ListEntry (remove #{""} (get-lines (.getAbsolutePath file))))
(speak (str "Enter the word " (first ListEntry)))
(.getAbsolutePath file)
(def current 0)))
(def Open-Action (action :handler (fn [e] (choose-file :type :open :selection-mode :files-only :dir ListDir :success-fn (fn [fc file](setup-list file))))
:name "Open"
:key "menu O"
:tip "Open list"))
(def entry-text(text :id :entry :text "" ))
(defn check-word[] ((if (= (text entry-text) (nth ListEntry current))(speak "correct")(speak "incorrect"))(def current (+ current 1))(speak (str "Enter the word " (nth ListEntry current)))))
(def Main-Panel(grid-panel :vgap 7 :hgap 20 :border "Main" :items[ (top-bottom-split entry-text
(left-right-split (grid-panel :vgap 7 :items[(label :text "Correct: 0") (label :text "Incorrect: 0")]) (grid-panel :vgap 7 :items[(button :text "Next"
:mnemonic \N
:listen [:action (fn [e] (check-word))])
(button :text "Repeat!"
:mnemonic \\
:listen [:action (fn[e] (speak(nth ListEntry current)))])
]) :divider-location 1/3 )
:divider-location 1/3 )]))
(def Main-Window(frame :title "??????", :on-close :exit, :size[425 :by 172]:menubar(menubar :items[(menu :text "File" :items [Open-Action ])]) :content Main-Panel ))
(show! Main-Window)
this is the error message I get when opening a file
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to clojure.lang.IFn
at clojure.lang.Var.fn(Var.java:378)
at clojure.lang.Var.invoke(Var.java:409)
at EKS.Core$setup_list.invoke(NO_SOURCE_FILE:38)
at EKS.Core$fn__5794$fn__5795.invoke(NO_SOURCE_FILE:42)
at seesaw.chooser$choose_file.doInvoke(chooser.clj:173)
at clojure.lang.RestFn.invoke(RestFn.java:619)
at EKS.Core$fn__5794.invoke(NO_SOURCE_FILE:42)
at seesaw.action$action$fn__1218.invoke(action.clj:90)
at seesaw.action.proxy$javax.swing.AbstractAction$0.actionPerformed(Unknown Source)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.AbstractButton.doClick(AbstractButton.java:376)
at javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:833)
at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:877)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:289)
at java.awt.Component.processMouseEvent(Component.java:6505)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
at java.awt.Component.processEvent(Component.java:6270)
at java.awt.Container.processEvent(Container.java:2229)
at java.awt.Component.dispatchEventImpl(Component.java:4861)
at java.awt.Container.dispatchEventImpl(Container.java:2287)
at java.awt.Component.dispatchEvent(Component.java:4687)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
at java.awt.Container.dispatchEventImpl(Container.java:2273)
at java.awt.Window.dispatchEventImpl(Window.java:2719)
at java.awt.Component.dispatchEvent(Component.java:4687)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:713)
at java.awt.EventQueue.access$000(EventQueue.java:104)
at java.awt.EventQueue$3.run(EventQueue.java:672)
at java.awt.EventQueue$3.run(EventQueue.java:670)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.awt.EventQueue$4.run(EventQueue.java:686)
at java.awt.EventQueue$4.run(EventQueue.java:684)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:683)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)
this is the test file I am using
this
here
word
What I believe is causing the traceback is:
(defn setup-list[ file](
(def ListEntry (remove #{""} (get-lines (.getAbsolutePath file))))
(speak (str "Enter the word " (first ListEntry)))
(.getAbsolutePath file)
(def current 0)))
Notice the paren after [ file]. You're calling def (which is probably not what you mean to do), but the result of that def is a lazy sequence (because of remove) and it being called as a function because of that paren. Unfortunately, it's not clear what you're trying to do with setup-list, otherwise I'd write something more idiomatic for you.
There a few other things you should consider. First, don't use (def ...) inside a function that's really bad form. In this case, I think what you're really after is (let ...):
(defn setup-list [file]
(let [entries (get-lines (.getAbsolutePath file))]
...))
It would also help you to see what's going on if you could keep a little more structure to the code. I think the formatting is letting things like that paren hide, where it may have been more obvious if you had a better style:
(defn setup-list [file]
((def ListEntry ...)
...))
Here, it's little more clear that the def will be used as a function call, which is probably not what you want.
BTW, Clojure has some Library Coding Standards. They've got some good things in there. And Ring is an excellent example to follow for a code base, as is Seesaw.