Here's my core code (sorry, some parts are in Portuguese)
(ns prova1-ed.core
(:gen-class))
(use 'clojure.java.io)
(defn getFile []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
filename
(do
(println "Nome de arquivo inválido. Digite novamente:")
(recur)))))
(defn getFileLines [^String filename]
(defn lista '())
(with-open [rdr (reader filename)]
(doseq [line (line-seq rdr)]
(if-not (= "" line)
(concat lista '(line)))
))
lista)
(defn -main [& args]
(println "Olá! Digite um arquivo contendo as contas bancárias:")
(getFileLines (getFile)))
prova1-ed.core> (-main)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: -main in this context, compiling:(C:\Users\Tiago\AppData\Local\Temp\form-init296650600503762010.clj:1:1)
I'm using cider, lein and Emacs. I haven't changed the project.clj. It was working yesterday (really).
If needed, I may provide more datails.
Edit
I tried lein run as suggested and I received
Unable to resolve symbol: filename in this context, compiling:(prova1_ed/core.clj:8:18)
filename was supposed to be the read-line result.
The error is on (defn lista '()). It should be (def lista '()).
It's working know.
(It's hard to debug using Emacs)
Thanks!
Related
I want to filter and modify output of tail command. This is what I come up with:
#!/usr/bin/env bb
(ns script
(:require
[clojure.java.io :as io]
[clojure.string :as str]
))
(->> (line-seq (io/reader *in*)
(filter #(re-find #"^\[.*CONSOLE" %))
(map #(str "carpenter " %)))
It works for normal tail. But I want to use it for "tail -f" command.
Any ideas?
Thx
This example starts writing to a file two kinds of messages: HELLO and BYE. Then it starts a tail -f process to watch the file and then reads from the output of that process and only captures the BYE lines and prints them with a custom string in front.
(ns tail-example
(:require [babashka.process :as p]
[clojure.java.io :as io]))
(future
(loop []
(spit "my-file.txt" "HELLO\n" :append true)
(spit "my-file.txt" "BYE\n" :append true)
(Thread/sleep 1)
(recur)))
(def tail (p/process
(p/tokenize "tail -f my-file.txt")
;; send stderr to stderr of bb, leave out stream unmodified
{:err :inherit}))
(let [rdr (io/reader (:out tail))]
(binding [*in* rdr]
(loop []
(when-let [l (read-line)]
(when (re-matches #"BYE" l)
(println (str "[log] " l)))
(recur)))))
I have the following code and it works when I go through and step through it in Cider, but when I run it straight through on the command line I get the following error:
Exception in thread "main" java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure-todo.todo/execute-todo-cmd, compiling:(/private/var/folders/d_/b8gmsvl16sgdg70m15gx20l40000gn/T/form-init6080372317525706515.clj:1:125)
at clojure.lang.Compiler.load(Compiler.java:7391)
at clojure.lang.Compiler.loadFile(Compiler.java:7317)
at clojure.main$load_script.invokeStatic(main.clj:275)
at clojure.main$init_opt.invokeStatic(main.clj:277)
at clojure.main$init_opt.invoke(main.clj:277)
at clojure.main$initialize.invokeStatic(main.clj:308)
at clojure.main$null_opt.invokeStatic(main.clj:342)
at clojure.main$null_opt.invoke(main.clj:339)
at clojure.main$main.invokeStatic(main.clj:421)
at clojure.main$main.doInvoke(main.clj:384)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure-todo.todo/execute-todo-cmd
Here is the code. I guess it's a namespace error, but I'm confused as to how it could be a namespace issue if it's working in cider.
(ns clojure-todo.todo
(:gen-class))
///// Other Code here
(defn print-msg-list [list]
(doseq [item list] (print-msg item)))
(defn create-todo [todo-string] (hash-map :todo-content todo-string :checked false))
(defn list-todos [todos]
(doseq [single-todo todos]
(do
(print (get single-todo :todo-content))
(let [is-checked (get single-todo :checked)]
(if is-checked
(print-msg :is-checked)
(print-msg :is-unchecked))))))
(defn add-todo []
(do
(print-msg :add-todo-message)
(let [todo-string (read-line) the-todo-created (create-todo todo-string)]
(def the-todos (conj the-todos the-todo-created))
(list-todos the-todos)))
(def todo-fns {"1" add-todo "2" delete-todo "3" list-todos "4" check-todo})
(defn execute-todo-cmd [command]
(get todo-fns command nil)))
(defn take-todo-command [backup-fn]
(let [command (read-line)]
(if (= command "q")
(print-msg :goodbye)
(let [todo-fn (execute-todo-cmd command)]
(if todo-fn
(todo-fn)
(do
(print-msg :sorry)
(print-msg :border)
(backup-fn)))))))
(defn run-app []
(do
(print-msg :welcome)
(print-msg-list '(:add-todo :list-todos :delete-todo :check-todo :quit))
;; The take-todo-command fn takes a backup in case the user types in
;; a command that's not supported by the application. In this case we just
;; want the app to restart
(take-todo-command run-app)))
This was just missing a close paren in add-todo, and there is an extra close paren in execute-todo-cmd. So execute-todo-cmd does not exist until add-todo is run, which is why it yields an unbound-fn error.
EDIT: Turned out I was using require instead of :require in the namespace declaration. With :require, tools.namespace refreshes the logging namespace, and the problem goes away. I still find it curious, however, that the expression (eval `(var ~(symbol "A/func"))) does not work in the situation described below (that is, if B below is not refreshed).
Summary: I'm using tools.namespace. If I have namespaces A and B, and in B do (eval `(var ~(symbol "A/func"))), and (tools.namespace/refresh) and run the code, that works. But if I make a change to A, do (tools.namespace/refresh), so that only A refreshes, then running that expression gives the error: Cannot resolve var: A/func in this context, even though A/func exists. Why?
Longer version:
In my project, I have a logging module/namespace that uses robert-hooke (see below). I'm using tools.namespace to reload my code when I make changes.
The problem is the following: When I want to log (my logging currently just prints) something, I list the functions that I want to log in my logging namespace and do (t.n/refresh). That works. But if I make changes to the the namespaces that contain the functions that I want to log, and do (t.n/refresh) without making changes to the logging namespace, the logging no longer works (for the functions that have been refreshed). As soon as I make a change to logging, so that it too is refreshed by tools.namespace, it starts working again.
So, it's like the vars in namespaces that have been refreshed don't properly get their logging hooks. But I don't understand why.
Below is my logging namespace. I call add-logging-wrappers each time I run my program.
If I add (eval `(var ~(symbol "sv/register-damage"))) inside add-logging-wrappers, that's fine when logging has just been refreshed and the logging works. But those times the logging does not work, that expression causes the error Cannot resolve var: sv/register-damage in this context.
(ns game.logging
(require [robert.hooke :as rh]
[clojure.pprint :as pp]
[game.server.core :as sv]
[game.client.core :as cl]
[game.math :as math]
(game.common [core-functions :as ccfns]
[graphics :as gfx])
(game.server [pathfinding :as pf]))
(:use [game.utils]))
(defn log-println [name type object]
(println (str (current-thread-name) " // " name " " type ":\n"
(with-out-str
(pp/pprint object)))))
(defn print-output [name f & args]
(let [result (apply f args)]
(log-println name "output" result)
result))
(defn print-input [name f & args]
(log-println name "input" args)
(apply f args))
(defn print-call [name f & args]
(println (str (current-thread-name) "//" name))
(apply f args))
(defmacro make-name-var-list [fn-list]
`[~#(for [fn fn-list]
[(str fn) `(var ~fn)])])
(defmacro defloglist [name & fns]
`(def ~name (make-name-var-list [~#fns])))
(defn add-hooks [name-vars & wrappers]
(when (seq wrappers)
(doseq [[name var] name-vars]
(rh/add-hook var (partial (first wrappers) name)))
(recur name-vars (next wrappers))))
(defn get-ns-name-vars [ns-sym]
(-> (the-ns ns-sym) (#(.name %)) ns-interns))
(defn add-hooks-to-ns [ns-sym & wrappers]
(apply add-hooks (get-ns-name-vars ns-sym) wrappers))
(defloglist log-both
sv/distribute-exp ;; <--- things to log
sv/register-damage
sv/give-exp)
(defloglist log-input)
(defloglist log-output)
(defn add-logging-wrappers []
(dorun (->> (all-ns) (map #(.name %)) (mapcat ns-interns) (map second)
(map rh/clear-hooks)))
(add-hooks log-both print-output print-input)
(add-hooks log-input print-input)
(add-hooks log-output print-output))
So, in my core.clj file I have:
(def page-buffer (BufferedReader. (InputStreamReader. (clojure.java.io/input-stream (clojure.java.io/resource "mitochondria.html")))))
(def parsed-page (atom ""))
and then later:
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page (with-open []
(.toString (reduce #(.append %1 %2)
(StringBuffer.) (line-seq page-buffer)))))
(println "Server is starting")
(println "port: " port)
(run-server port)))
This compiles and then I turn it into an uberjar. But when I run it I get the line with swap! blows up:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn
at clojure.core$swap_BANG_.invoke(core.clj:2106)
at serve_pages_from_memory.core$_main.doInvoke(core.clj:29)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at serve_pages_from_memory.core.main(Unknown Source)
I tried this without using an atom and everything worked just fine (using a var defined with "def" to be a string) but eventually I want to send this var to multiple threads, so I need for this to be an atom or agent.
What have I done wrong?
UPDATE:
Jeremy Heiler, thanks. Fixed, but the result is ugly as sin:
(defn parse-buffer [& everything-else]
(with-open []
(.toString (reduce #(.append %1 %2)
(StringBuffer.) (line-seq page-buffer)))))
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page parse-buffer)
(println "Server is starting")
(println "port: " port)
(run-server port)))
I have to give an argument to parse-buffer or I get an error. But I do not use the argument, so this is ugly. I must be writing this wrong, yes?
You need to pass a function to swap!. Right now you are passing it a value.
(let [foo (atom 1)]
(swap! foo + 2)
#foo)
The above expression will return 3. The passed in function takes the current value of the atom, and the return value becomes the new value of the atom. Any extra arguments are passed to the given function.
To comment on your update: Yes, you do need to give an argument to the function used in swap!. The first argument is the current value of the the atom. However, you don't need to hack parse-buffer in order to make it work. You can just wrap it with an anonymous function.
Also, parse-buffer can be simplified greatly. Since line-seq returns a sequence of lines from the given reader, and str uses a StringBuilder internally, you can just apply it over the sequence.
(defn parse-buffer []
(with-open [buf page-buffer]
(apply str (line-seq buf))))
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page (fn [cur-val] (parse-buffer)))
(println "Server is starting")
(println "port: " port)
(run-server port)))
two files
types.clj:
(ns test.types)
(defrecord Price [date price])
(defrecord ProductPrice [name prices])
core.clj (It's OK)
(ns test.core
(:use [test.types])
(:use [clojure.string :only (split)]))
(defn read-data [file]
(let [name (subs (.getName file) 0 4)]
(with-open [rdr (clojure.java.io/reader file)]
(doall (map #(apply ->Price (split % #"\t")) (drop 2 (line-seq rdr)))))))
core.clj (java.lang.IllegalArgumentException: Unable to resolve classname: ProductPrice)
(ns test.core
(:use [test.types])
(:use [clojure.string :only (split)]))
(defn read-data [file]
(let [name (subs (.getName file) 0 4)]
(with-open [rdr (clojure.java.io/reader file)]
(ProductPrice. name (doall (map #(apply ->Price (split % #"\t")) (drop 2 (line-seq rdr))))))))
core.clj (It's OK)
(ns test.core
(:use [test.types])
(:use [clojure.string :only (split)]))
(defrecord tProductPrice [name prices])
(defn read-data [file]
(let [name (subs (.getName file) 0 4)]
(with-open [rdr (clojure.java.io/reader file)]
(tProductPrice. name (doall (map #(apply ->Price (split % #"\t")) (drop 2 (line-seq rdr)))))))
core.clj (java.lang.IllegalStateException: ->ProductPrice already refers to: #'test.types/->ProductPrice in namespace: test.core)
(ns test.core
(:use [test.types])
(:use [clojure.string :only (split)]))
(defrecord ProductPrice [name prices])
(defn read-data [file]
(let [name (subs (.getName file) 0 4)]
(with-open [rdr (clojure.java.io/reader file)]
(ProductPrice. name (doall (map #(apply ->Price (split % #"\t")) (drop 2 (line-seq rdr)))))))
I totally confused about these exceptions. And I can't find any more usage about 'record' except some simplest examples from clojure.org and books.
Any help, Thank you very much!
defrecord creates a java class in the package named after the current namespace. (ProductPrice. ...) is a call to the constructor of that type; this is java interop - not a plain function call.
You cannot refer to a class defined outside of java.lang or the current namespace unless you explicitly import it or specify the full package name. This includes calling its constructor.
So, to fix the problem you need to import Price and ProductPrice.
(ns test.core (:import [test.types Price]))
(Price. ...)
or call the full class+package name:
(test.types.Price. ...)