Need function or macro, which takes const parameter(route) and dynamic parameters (args) and return concatenated string of parameters:
user>(defn full-url [route & args] *need code* )
#'user/full-url
user> (def p1 "value1")
#'user/p1
user> (def p2 "value2")
#'user/p2
user> (def p3 "value3")
#'user/p3
user> (full-url "/init" p1 p2 p3)
"/init?p1=value1&p2=value2&p3value4"
Any ideas?
First, a macro to do what you want:
(defmacro full-url
[route & args]
`(let [var-names# (map #(str %1 "=") '~args)
var-vals# (list ~#args)
joined# (clojure.string/join "&" (map str var-names# var-vals#))]
(str ~route "?" joined#)))
Now, I would add that I do not think this is the best approach as it ties the names of your vars to the param names. IMO a better approach is to use a regular function that takes a map as a second argument, that has keywords and values. Such as:
(defn full-url-fn
[route params]
(->> params
(map #(str (name (first %)) "=" (second %)))
(clojure.string/join "&")
(str route "?")))
(full-url-fn "/init" {:p1 "value1" :p2 "value2"})
But, either way should work.
Related
I'm looking for something like join but with the delimiter going in front of each string rather than just acting as a separator.
As a simple example, I'm looking for a less ugly version of this:
(def params [1 2 3 4])
(clojure.string/join (for [x params] (str "¶m=" x)))
result
"¶m=1¶m=2¶m=3¶m=4"
Calling (clojure.string/join coll) with no separator is the same as (apply str coll) which is a tiny win:
(apply str (for [x params] (str "¶m=" x)))
Then you might prefer map over for here:
(apply str (map #(str "¶m=" %) params))
interleave could work:
(apply str (interleave (repeat "¶m=") params))
You could refactor this to separate the prefix and interleave it with the strings:
(apply str
(interleave (repeat \&)
(map #(str "param=" %) params)))
You might like the look of threading:
(->> (map #(str "param=" %) params)
(interleave (repeat \&))
(apply str))
You could extract a function to do this more generally:
(defn prepend-join [separator & cs]
(apply str (apply interleave (repeat separator) cs)))
(prefix-join \& (map #(str "param=" %) params))
and in addition:
there is a handy function in clojure's core lib, namely clojure.pprint/cl-format
user> (clojure.pprint/cl-format nil "~{¶m=~a~}" [1 2 3 4 5])
;;=> "¶m=1¶m=2¶m=3¶m=4¶m=5"
talking about it's capabilities, it is just the top of the iceberg
Prepending an empty string to params looks pretty clean to me:
(clojure.string/join "¶m=" (cons "" params))
;;=> "¶m=1¶m=2¶m=3¶m=4"
I'm writing a macro to allow pass the clauses as a parameter to functions:
(defmacro parse-cmd [command & body]
(let [parts (str/split command #" ")
cmd (first parts)
args (into [] (rest parts))
clauses (partition 2 2 body)]
`(case ~cmd
~#(mapcat (fn [c] [(nth c 0) `(apply ~(nth c 1) ~args)]) clauses))))
(defn mysum [a b]
(+ (Integer. a) (Integer. b)))
(parse-cmd "SET 1 1" "SET" "GET" println)
2
This works well when cmd is a string, however with a var:
(def cmd "SET 1 1")
(parse-cmd cmd "SET" "GET" println)
I get ClassCastException clojure.lang.Symbol cannot be cast to java.lang.CharSequenceq clojure.string/split (string.clj:222)
I guess I should prevent the evaluation of the let too, but I can't make it work:
(defmacro parse-cmd [command & body]
`(let [parts# (str/split ~command #" ")
cmd# (first parts#)
args# (into [] (rest parts#))
clauses# (partition 2 2 ~body)]
(case cmd#
(mapcat (fn [c#] [(nth c# 0) `(apply ~(nth c# 1) args#)]) clauses#))))
With this definition I get:
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn kvstore.replication/eval12098 (form-init7453673077215360561.clj:1)
let's macroexpand this (for your second macro)
(parse-cmd "SET 1 1" "SET" mysum "GET" println)
it expands to:
(let [parts__31433__auto__ (str/split "SET 1 1" #" ")
cmd__31434__auto__ (first parts__31433__auto__)
args__31435__auto__ (into [] (rest parts__31433__auto__))
clauses__31436__auto__ (partition
2
2
("SET" mysum "GET" println))]
(case
cmd__31434__auto__
(mapcat
(fn [c__31437__auto__] [(nth c__31437__auto__ 0)
(seq
(concat
(list 'apply)
(list (nth c__31437__auto__ 1))
(list 'args__31432__auto__)))])
clauses__31436__auto__)))
there are two problems here:
1) you generate this code: ("SET" mysum "GET" println), which obviously causes your exception, because "SET" is not a function
2) you generate the wrong case expression, I see that you have forgotten to unquote-splice your mapcat
Let's try to fix this:
first of all unquote mapcat; then you can move clauses out of your generated let, because it can be totally done at compile-time:
(defmacro parse-cmd [command & body]
(let [clauses (partition 2 2 body)]
`(let [parts# (str/split ~command #" ")
cmd# (first parts#)
args# (into [] (rest parts#))]
(case cmd#
~#(mapcat (fn [c] [(nth c 0) `(apply ~(nth c 1) args#)]) clauses)))))
now let's check the expansion:
(let [parts__31653__auto__ (str/split "SET 1 1" #" ")
cmd__31654__auto__ (first parts__31653__auto__)
args__31655__auto__ (into [] (rest parts__31653__auto__))]
(case
cmd__31654__auto__
"SET"
(apply mysum args__31652__auto__)
"GET"
(apply println args__31652__auto__)))
ok. looks better. let's try to run it:
(parse-cmd "SET 1 1" "SET" mysum "GET" println)
we have another error now:
CompilerException java.lang.RuntimeException: Unable to resolve symbol: args__31652__auto__ in this context, compiling:(*cider-repl ttask*:2893:12)
so expansion also shows us this:
args__31655__auto__ (into [] (rest parts__31653__auto__))
...
(apply mysum args__31652__auto__)
so there are different symbols for args# here. That's because the scope of the generated symbol name is one syntax-quote. So inner syntax-quote with apply generates the new one. You should use gensym to fix that:
(defmacro parse-cmd [command & body]
(let [clauses (partition 2 2 body)
args-sym (gensym "args")]
`(let [parts# (str/split ~command #" ")
cmd# (first parts#)
~args-sym (into [] (rest parts#))]
(case cmd#
~#(mapcat (fn [c] [(nth c 0) `(apply ~(nth c 1) ~args-sym)]) clauses)))))
ok now it should work properly:
ttask.core> (parse-cmd "SET 1 1" "SET" mysum "GET" println)
2
ttask.core> (parse-cmd cmd "SET" mysum "GET" println)
2
great!
I would also recommend you to use destructuring in a mapcat function and quoted let, to make it more readable:
(defmacro parse-cmd [command & body]
(let [clauses (partition 2 2 body)
args-sym (gensym "args")]
`(let [[cmd# & ~args-sym] (str/split ~command #" ")]
(case cmd#
~#(mapcat (fn [[op fun]] [op `(apply ~fun ~args-sym)]) clauses)))))
But if it's not just an exercise in writing macros, you shouldn't use the macro for that, since you pass just string and function references here, so anyway you shall evaluate everything in runtime.
(defn parse-cmd-1 [command & body]
(let [[cmd & args] (str/split command #" ")
commands-map (apply hash-map body)]
(apply (commands-map cmd) args)))
I'm working on problem #74 at 4clojure.com, the solution of mine is as following:
(defn FPS [s]
(->>
(map read-string (re-seq #"[0-9]+" s))
(filter #(= (Math/sqrt %) (Math/floor (Math/sqrt %))))
(interpose ",")
(apply str)))
It works pretty well. But if I use the "thread-first" macro ->
(defn FPS [s]
(->
(map read-string (re-seq #"[0-9]+" s))
(filter #(= (Math/sqrt %) (Math/floor (Math/sqrt %))))
(interpose ",")
(apply str)))
It returns: ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.IFn clojure.core/apply (core.clj:617)
Why can "->>" not be replaced by "->" in this problem?
Thread-last macro (->>) inserts each for as the last element of the next form. Thread-first macro (->) inserts it as the second element.
So, this:
(->> a
(b 1)
(c 2))
translates to: (c 2 (b 1 a)), while
(-> a
(b 1)
(c 2))
translates to: (c (b a 1) 2).
In Clojure REPL:
user=> (doc ->)
-------------------------
clojure.core/->
([x & forms])
Macro
Threads the expr through the forms. Inserts x as the
second item in the first form, making a list of it if it is not a
list already. If there are more forms, inserts the first form as the
user=> (doc ->>)
-------------------------
clojure.core/->>
([x & forms])
Macro
Threads the expr through the forms. Inserts x as the
last item in the first form, making a list of it if it is not a
list already. If there are more forms, inserts the first form as the
last item in second form, etc.
filter function expects the first argument to be a function, not a sequence and by using S-> , you are not satisfying its requirement.
That is why you are getting clojure.lang.LazySeq cannot be cast to clojure.lang.IFn exception in your code.
4Clojure Problem 58 is stated as:
Write a function which allows you to create function compositions. The parameter list should take a variable number of functions, and create a function applies them from right-to-left.
(= [3 2 1] ((__ rest reverse) [1 2 3 4]))
(= 5 ((__ (partial + 3) second) [1 2 3 4]))
(= true ((__ zero? #(mod % 8) +) 3 5 7 9))
(= "HELLO" ((__ #(.toUpperCase %) #(apply str %) take) 5 "hello world"))
Here __ should be replaced by the solution.
In this problem the function comp should not be employed.
A solution I found is:
(fn [& xs]
(fn [& ys]
(reduce #(%2 %1)
(apply (last xs) ys) (rest (reverse xs)))))
It works. But I don't really understand how the reduce works here. How does it represent (apply f_1 (apply f_2 ...(apply f_n-1 (apply f_n args))...)?
Let's try modifying that solution in 3 stages. Stay with each for a while and see if you get it. Stop if and when you do lest I confuse you more.
First, let's have more descriptive names
(defn my-comp [& fns]
(fn [& args]
(reduce (fn [result-so-far next-fn] (next-fn result-so-far))
(apply (last fns) args) (rest (reverse fns)))))
then factor up some
(defn my-comp [& fns]
(fn [& args]
(let [ordered-fns (reverse fns)
first-result (apply (first ordered-fns) args)
remaining-fns (rest ordered-fns)]
(reduce
(fn [result-so-far next-fn] (next-fn result-so-far))
first-result
remaining-fns))))
next replace reduce with a loop which does the same
(defn my-comp [& fns]
(fn [& args]
(let [ordered-fns (reverse fns)
first-result (apply (first ordered-fns) args)]
(loop [result-so-far first-result, remaining-fns (rest ordered-fns)]
(if (empty? remaining-fns)
result-so-far
(let [next-fn (first remaining-fns)]
(recur (next-fn result-so-far), (rest remaining-fns))))))))
My solution was:
(fn [& fs]
(reduce (fn [f g]
#(f (apply g %&))) fs))
Lets try that for:
((
(fn [& fs]
(reduce (fn [f g]
#(f (apply g %&))) fs))
#(.toUpperCase %)
#(apply str %)
take)
5 "hello world"))
fs is a list of the functions:
#(.toUpperCase %)
#(apply str %)
take
The first time through the reduce, we set
f <--- #(.toUpperCase %)
g <--- #(apply str %)
We create an anonymous function, and assign this to the reduce function's accumulator.
#(f (apply g %&)) <---- uppercase the result of apply str
Next time through the reduce, we set
f <--- uppercase the result of apply str
g <--- take
Again we create a new anonymous function, and assign this to the reduce function's accumulator.
#(f (apply g %&)) <---- uppercase composed with apply str composed with take
fs is now empty, so this anonymous function is returned from reduce.
This function is passed 5 and "hello world"
The anonymous function then:
Does take 5 "hello world" to become (\h \e \l \l \o)
Does apply str to become "hello"
Does toUppercase to become "HELLO"
Here's an elegent (in my opinion) definition of comp:
(defn comp [& fs]
(reduce (fn [result f]
(fn [& args]
(result (apply f args))))
identity
fs))
The nested anonymous functions might make it hard to read at first, so let's try to address that by pulling them out and giving them a name.
(defn chain [f g]
(fn [& args]
(f (apply g args))))
This function chain is just like comp except that it only accepts two arguments.
((chain inc inc) 1) ;=> 3
((chain rest reverse) [1 2 3 4]) ;=> (3 2 1)
((chain inc inc inc) 1) ;=> ArityException
The definition of comp atop chain is very simple and helps isolate what reduce is bringing to the show.
(defn comp [& fs]
(reduce chain identity fs))
It chains together the first two functions, the result of which is a function. It then chains that function with the next, and so on.
So using your last example:
((comp #(.toUpperCase %) #(apply str %) take) 5 "hello world") ;=> "HELLO"
The equivalent only using chain (no reduce) is:
((chain identity
(chain (chain #(.toUpperCase %)
#(apply str %))
take))
5 "hello world")
;=> "HELLO"
At a fundamental level, reduce is about iteration. Here's what an implementation in an imperative style might look like (ignoring the possibility of multiple arities, as Clojure's version supports):
def reduce(f, init, seq):
result = init
for item in seq:
result = f(result, item)
return result
It's just capturing the pattern of iterating over a sequence and accumulating a result. I think reduce has a sort of mystique around it which can actually make it much harder to understand than it needs to be, but if you just break it down you'll definitely get it (and probably be surprised how often you find it useful).
Here is my solution:
(defn my-comp
([] identity)
([f] f)
([f & r]
(fn [& args]
(f (apply (apply my-comp r) args)))))
I like A. Webb's solution better, though it does not behave exactly like comp because it does not return identity when called without any arguments. Simply adding a zero-arity body would fix that issue though.
Consider this example:
(def c (comp f1 ... fn-1 fn))
(c p1 p2 ... pm)
When c is called:
first comp's rightmost parameter fn is applied to the p* parameters ;
then fn-1 is applied to the result of the previous step ;
(...)
then f1 is applied to the result of the previous step, and its result is returned
Your sample solution does exactly the same.
first the rightmost parameter (last xs) is applied to the ys parameters:
(apply (last xs) ys)
the remaining parameters are reversed to be fed to reduce:
(rest (reverse xs))
reduce takes the provided initial result and list of functions and iteratively applies the functions to the result:
(reduce #(%2 %1) ..init.. ..functions..)
How can I convert the values of 'mymap' to a list of Doubles instead of a list of Strings, at the same time as mymap is created?
(use '[clojure.string :only (join split)])
;(def raw-data (slurp "http://ichart.finance.yahoo.com/table.csv?s=INTC"))
;Downloaded and removed the first line
(def raw-data (slurp "table-INTC.csv"))
(def raw-vector-list
(map
#(split % #",") ; anonymous map function to split by comma
(split raw-data #"\n"))) ; split raw data by new line
(pr (take 1 raw-vector-list))
(def mymap
(zipmap
;construct composite key out of symbol and date which is head of the list
(map #(str "INTC-" %) (map first raw-vector-list))
;How do i convert these values to Double instead of Strings?
(map rest raw-vector-list)))
(pr (take 1 mymap))
(def mymap
(zipmap
(map #(str "NAT-" %) (map first raw-vector-list))
(map #(map (fn [v] (Double/parseDouble v)) %)
(map rest raw-vector-list))))
(pprint (take 1 mymap))
-> (["NAT-1991-09-30" (41.75 42.25 41.25 42.25 3.62112E7 1.03)])
Another version
(def mymap
(map (fn [[date & values]]
[(str "NAT-" date)
(map #(Double/parseDouble %) values)])
;; Drop first non-parsable element in raw-vector-list
;; ["Date" "Open" "High" "Low" "Close" "Volume" "Adj Close"]
(drop 1 raw-vector-list)))
So for the tail/rest portion of this data. You are mapping an anonymous, map function, to a list of strings, and then mapping the type conversion to the elements in each sublist.
(def mymap
(zipmap
(map #(str "NAT-" %) (map first raw-vector-list))
(map #(map (fn [v] (Double/parseDouble v)) %)
(map rest raw-vector-list))))
How can I pull out the type conversion into a function like below...And then utilize my custom method?
(defn str-to-dbl [n] (Double/parseDouble n))
This code complains about nested #'s.
(def mymap
(zipmap
(map #(str "NAT-" %) (map first raw-vector-list))
(map #(map #(str-to-double %)
(map rest raw-vector-list))))