I use "intern" function for generate runtime funtions, but I can't test generted function.
In core.clj I defined functions that generate runtime functions like this.
(ns cftf.core)
(defn- generation-function [function-name ratio]
(let [function-symbol (symbol function-name)]
(intern 'cftf.core function-symbol (fn [input] (cftf.core/convert ratio input)))))
...
(defn generate-function-by-units [units]
(->> units
(units->function-name-and-ratio)
(map #(generation-function (first %) (second %)))))
I wrote the following test code.
(ns cftf.core-test
(:require [clojure.test :refer :all]
[cftf.core :as c]))
(defn amount-of-alcohol-per-one-bottle [bottle-size abv]
(* bottle-size (/ abv 100)))
(def alcohol-units
{:soju (amount-of-alcohol-per-one-bottle 360 16.5)
:beer (amount-of-alcohol-per-one-bottle 500 5)
:whisky (amount-of-alcohol-per-one-bottle 700 40)
})
(c/generate-function-by-units alcohol-units)
(deftest a-test
(testing "soju->beer test"
(is (= (int (c/soju->beer 1)) 2))))
After calling "generate-function-by-units" in the repl, you can use the "soju->beer" function.
But in the test I get the error "No such var: c/soju->beer".
I've tried using a fixture or calling " generate-function-by-units" the top in test.clj, but I'm still getting the error.
Related
I am using [org.clojure/clojure "1.10.1"],[org.clojure/core.async "1.2.603"] and the latest Amazon Corretto 11 JVM if there was anything to do with them.
The following code is a simplified version of the code used in production and it does cause memory leak. I have no idea why that happened but suspect it might due to sub/unsub of channels. Can anyone help point out where my code may go wrong or how I can fix the memory leak?
(ns test-gc.core
(:require [clojure.core.async :as a :refer [chan put! close! <! go >! go-loop timeout]])
(:import [java.util UUID]))
(def global-msg-ch (chan (a/sliding-buffer 200)))
(def global-msg-pub (a/pub global-msg-ch :id))
(defn io-promise []
(let [id (UUID/randomUUID)
ch (chan)]
(a/sub global-msg-pub id ch)
[id (go
(let [x (<! ch)]
(a/unsub global-msg-pub id ch)
(:data x)))]))
(defn -main []
(go-loop []
(<! (timeout 1))
(let [[pid pch] (io-promise)
cmd {:id pid
:data (rand-int 1E5)}]
(>! global-msg-ch cmd)
(println (<! pch)))
(recur))
(while true
(Thread/yield)))
A quick heap dump gives the following statistics for example:
Class by number of instances
java.util.LinkedList 5,157,128 (14.4%)
java.util.concurrent.atomic.AtomicReference 3,698,382 (10.3%)
clojure.lang.Atom 3,094,279 (8.6%)
...
Class by size of instances
java.lang.Object[] 210,061,752 B (13.8%)
java.util.LinkedList 206,285,120 B (13.6%)
clojure.lang.Atom 148,525,392 B (9.8%)
clojure.core.async.impl.channels.ManyToManyChannel 132,022,336 B (8.7%)
...
I finally figured out why. By looking at the source code, we get the following segment:
(defn pub
"Creates and returns a pub(lication) of the supplied channel, ..."
...
(let [mults (atom {}) ;;topic->mult
ensure-mult (fn [topic]
(or (get #mults topic)
(get (swap! mults
#(if (% topic) % (assoc % topic (mult (chan (buf-fn topic))))))
topic)))
p (reify
Mux
(muxch* [_] ch)
Pub
(sub* [p topic ch close?]
(let [m (ensure-mult topic)]
(tap m ch close?)))
(unsub* [p topic ch]
(when-let [m (get #mults topic)]
(untap m ch)))
(unsub-all* [_] (reset! mults {}))
(unsub-all* [_ topic] (swap! mults dissoc topic)))]
...
p)))
We can see mults stores all topic hence shall increase monotonically if we do not clear it. We may add something like (a/unsub-all* global-msg-pub pid) to fix that.
i have one file "map_reduce2.clj" and another "map_reduce3.clj", the both defin function "map-reduce" themself.
now i want to use namespace of "map_reduce2.clj" in "map_reduce3.clj", but when i press "C-c C-k" in emacs to compile the "map_reduce3.clj",
error happens: "parse-line already refers to: #'chapter12.map-reduce2/parse-line in
namespace: chapter12.map-reduce3" , but this doesn't make any sense.
; map_reduce3.cli
(ns chapter12.map-reduce3
(:use clojure.java.io)
(:require [chapter12.map-reduce2 :as c12]))
(def IGNORE "_")
(defn parse-line [line]
(let [tokens (.split (.toLowerCase line) " ")]
[[IGNORE (count tokens)]]))
(defn average [numbers]
(/ (apply + numbers)
(count numbers)))
(defn reducer [combined]
(average (val (first combined))))
(defn average-line-length [filename]
(c12/map-reduce parse-line reducer (line-seq (reader filename))))
; map_reduce2.clj
(ns chapter12.map-reduce2
(:use clojure.java.io))
(defn combine [mapped]
(->> (apply concat mapped)
(group-by first)
(map (fn [[k v]]
{k (map second v)}))
(apply merge-with conj)))
(defn map-reduce [mapper reducer args-seq]
(->> (map mapper args-seq)
(combine)
(reducer)))
(defn parse-line [line]
(let [tokens (.split (.toLowerCase line) " ")]
(map #(vector % 1) tokens)))
(defn sum [[k v]]
{k (apply + v)})
(defn reduce-parsed-lines [collected-values]
(apply merge (map sum collected-values)))
(defn word-frequency [filename]
(map-reduce parse-line reduce-parsed-lines (line-seq (reader filename))))
images of the the error
This probably means you have dirty REPL state. Maybe you moved the function parse-line from one namespace to the other. I suggest restarting the REPL, or unload parse-line from map-reduce3: How to unload a function from another namespace?.
I have a namespace like this:
(ns foo.core)
(def ^:dynamic *debug-fn*
"A function taking arguments [bar baz]"
nil)
(defn bar-info
[bar _]
(println bar))
(defn baz-info
[_ baz]
(println baz))
(defn do-stuff
[bar baz]
(when *debug-fn* (*debug-fn* bar baz)))
(defn -main
[& {:keys [debug-fn]}]
(binding [*debug-fn* (symbol debug-fn)] ;; THIS WON'T WORK!
(do-stuff 27 42)))
What I would like to do is allow a debug function to be specified from the command line like this: lein run bar-info or lein run baz-info.
I'm not sure how to take the string specified as a command-line argument and turn it into the namespace-qualified function to bind. Do I need a macro to do this?
Use ns-resolve, you will need to specify namespace where your function is defined though.
user=> (defn f [n] (* n n n))
#'user/f
user=> ((ns-resolve *ns* (symbol "f")) 10)
1000
Use alter-var-root:
user=> (doc alter-var-root)
-------------------------
clojure.core/alter-var-root
([v f & args])
Atomically alters the root binding of var v by applying f to its
current value plus any args
nil
user=> (alter-var-root #'*debug-fn* (fn [v] (fn [x] (println x) x)))
#<user$eval171$fn__172$fn__173 user$eval171$fn__172$fn__173#7c93d88e>
user=> (*debug-fn* 1)
1
1
Though I've accepted Guillermo's answer above, I figured that it might also be useful to add the solution I ended up going with:
(def debug-fns
{:bar-info (fn [bar _] (println bar))
:baz-info (fn [_ baz] (println baz))
(def active-debug-fns (atom []))
(defn activate-debug-fn!
[fn-key]
(let [f (debug-fns fn-key)]
(if f
(swap! active-debug-fns conj f)
(warn (str "Debug function " fn-key " not found! Available functions are: "
(join " " (map name (keys debug-fns))))))))
(defn debug-fn-keys
[args]
(if (args "--debug")
(split (or (args "--debug") "") #",")
[]))
(defn do-stuff
[bar baz]
(doseq [f #active-debug-fns]
(f bar baz)))
(defn -main
[& args]
(let [args (apply hash-map args)]
(doseq [f (debug-fn-keys args)]
(activate-debug-fn! (keyword k)))
(do-stuff 27 42)))
So now you can say something like lein run --debug bar-info to get info on bars, or lein run --debug bar,baz to get info on both bars and bazes.
Any suggestions to make this more idiomatic will be happily accepted and edited in. :)
I am trying to write a cycle based Fizz Buzz in clojure. It seems to work for the values that are not Fizz or Buzz, but for the values that are Fizz and Buzz it returns nil.
Code:
(ns fizz-buzz.core
(:gen-class))
(defn fizz-buzz [value]
(let [fizz (cycle ["" "" "Fizz"])
buzz (cycle ["" "" "" "" "Buzz"])
fb (map str fizz buzz)]
(nth (map-indexed
(fn [i v]
(if (clojure.string/blank? v)
(str (+ i 1)
v)))
fb)
(- value 1)))
Tests:
(ns fizz-buzz.core-test
(:require [clojure.test :refer :all]
[fizz-buzz.core :refer :all]))
(deftest value-2-will-return-2
(testing "2 will return the string 2"
(is (= "2" (fizz-buzz 2)))))
(deftest value-4-will-return-4
(testing "4 will return the string 4"
(is (= "4" (fizz-buzz 4)))))
(deftest value-3-will-return-fizz
(testing "3 will return the string Fizz"
(is (= "Fizz" (fizz-buzz 3)))))
(deftest value-5-will-return-buzz
(testing "5 will return the string Buzz"
(is (= "Buzz" (fizz-buzz 5))))
The first two tests work (2 and 4), but the Fizz and Buzz test do not work. I am sure I do not understand how map-indexed works.
You have a very slight bracket misplacement in your if statement.
This means that you have no else case to your if (hence the nil result)
Try rewriting:
(if (clojure.string/blank? v)
(str (+ i 1)
v)))
As:
(if (clojure.string/blank? v)
(str (+ i 1))
v))
ps - it looks like you've understood map-indexed fine :)
I know I can do (:use function) but how do I do this for a multimethod?
Multimethods are used from other namespaces in the same way as functions.
If you have the following in com/example/foo.clj
(ns com.example.foo)
(defn f [x]
(* x x))
(defmulti m first)
(defmethod m :a [coll]
(map inc (rest coll)))
In the file com/example/bar.clj you can use both f and m in the same manner:
(ns com.example.bar
(:use [com.example.foo :only [f m]]))
(defn g []
(println (f 5)) ; Call the function
(println (m [:a 1 2 3]))) ; Call the multimethod
;; You can also define new cases for the multimethod defined in foo
;; which are then available everywhere m is
(defmethod m :b [coll]
(map dec (rest coll)))
I hope this answers your question!