clojure macro variable trouble with map - clojure

I'm having trouble getting a clojure defmacro to do what I want. I've reduced my actual code down to the following snippet.
This creates something close to what I want. I am trying to conditionally insert either (first p#) or (second p#) depending on a parameter passed into the macro.
(defmacro mmz1 [t]
`(map (fn [p#] (let [t1# (first p#)
t2# ~(if t `(first p#) `(second p#))]
(* t1# t2#)))
[ [1 2] [3 4] ]))
(macroexpand-1 '(mmz1 false))
shows
(map
(fn [p__18341__auto__]
(let [t1__18342__auto__ (first p__18341__auto__)
t2__18343__auto__ (second p__18340__auto__)]
(* t1__18342__auto__ t2__18343__auto__)))
[[1 2] [3 4]])
But, note that the variable in this form (second p_18340_auto_) does not match the anonymous function argument p_18341_auto_. So, executing the code results in an error since that second var is not defined. How can I get these vars to match? This is what I would like to accomplish.
For testing purposes, this code accomplishes what I want, but I don't want the (if) form in the resulting macro code that sets t2#. A macro should allow me to do this--shouldn't it?
(defmacro mmz0 [t]
`(map (fn [p#] (let [t1# (first p#)
t2# (if ~t (first p#) (second p#))]
(* t1# t2#)))
[ [1 2] [3 4] ]))
(macroexpand-1 '(mmz0 false))
shows
(map
(fn [p__18387__auto__]
(let [t1__18388__auto__ (first p__18387__auto__)
t2__18389__auto__ (if false
(first p__18387__auto__)
(second p__18387__auto__))]
(* t1__18388__auto__ t2__18389__auto__)))
[[1 2] [3 4]])
and the output of the code is the expected:
(mmz0 false) -> (2 12)

One of the solutions
(defmacro mmz1 [t]
`(map (fn [p#] (let [t1# (first p#)
t2# (~(if t 'first 'second) p#)]
(* t1# t2#)))
[ [1 2] [3 4] ]))
Update. More general solution
(defmacro mmz1 [t]
(let [trg-fn (if t
`(fn [p#] (first p#))
`(fn [p#] (second p#)))]
`(map (fn [p#] (let [t1# (first p#)
t2# (~trg-fn p#)]
(* t1# t2#)))
[ [1 2] [3 4] ])))
You can replace (fn [p#]...) in if branches with more complex functions.
Update2. Simpler solution that uses predefined function formal parameter p
(defmacro mmz1 [t]
(let [p `p#]
`(map (fn [~p] (let [t1# (first ~p)
t2# ~(if t `(first ~p) `(second ~p))]
(* t1# t2#)))
[ [1 2] [3 4] ])))

It has to be stated that this has no need to be a macro at all.
The function equivalent is more readable and more composable, so I recommend you use that.

Related

Make (map f c1 c2) map (count c1) times, even if c2 has less elements

When doing
(map f [0 1 2] [:0 :1])
f will get called twice, with the arguments being
0 :0
1 :1
Is there a simple yet efficient way, i.e. without producing more intermediate sequences etc., to make f get called for every value of the first collection, with the following arguments?
0 :0
1 :1
2 nil
Edit Addressing question by #fl00r in the comments.
The actual use case that triggered this question needed map to always work exactly (count first-coll) times, regardless if the second (or third, or ...) collection was longer.
It's a bit late in the game now and somewhat unfair after having accepted an answer, but if a good answer gets added that only does what I specifically asked for - mapping (count first-coll) times - I would accept that.
You could do:
(map f [0 1 2] (concat [:0 :1] (repeat nil)))
Basically, pad the second coll with an infinite sequence of nils. map stops when it reaches the end of the first collection.
An (eager) loop/recur form that walks to end of longest:
(loop [c1 [0 1 2] c2 [:0 :1] o []]
(if (or (seq c1) (seq c2))
(recur (rest c1) (rest c2) (conj o (f (first c1) (first c2))))
o))
Or you could write a lazy version of map that did something similar.
A general lazy version, as suggested by Alex Miller's answer, is
(defn map-all [f & colls]
(lazy-seq
(when-not (not-any? seq colls)
(cons
(apply f (map first colls))
(apply map-all f (map rest colls))))))
For example,
(map-all vector [0 1 2] [:0 :1])
;([0 :0] [1 :1] [2 nil])
You would probably want to specialise map-all for one and two collections.
just for fun
this could easily be done with common lisp's do macro. We could implement it in clojure and do this (and much more fun things) with it:
(defmacro cl-do [clauses [end-check result] & body]
(let [clauses (map #(if (coll? %) % (list %)) clauses)
bindings (mapcat (juxt first second) clauses)
nexts (map #(nth % 2 (first %)) clauses)]
`(loop [~#bindings]
(if ~end-check
~result
(do
~#body
(recur ~#nexts))))))
and then just use it for mapping (notice it can operate on more than 2 colls):
(defn map-all [f & colls]
(cl-do ((colls colls (map next colls))
(res [] (conj res (apply f (map first colls)))))
((every? empty? colls) res)))
in repl:
user> (map-all vector [1 2 3] [:a :s] '[z x c v])
;;=> [[1 :a z] [2 :s x] [3 nil c] [nil nil v]]

How to write a reader macro to transfer the code in Clojure?

I want to write a macro named $=> to transfer the code like:
(let [bb 11] ($=> #"aa#{bb}")) => ["aa?" 11]
which means that I want to splite all string after # with the pattern #\{.*?\}
and replace the pattern with ? and then eval the symbal of the patten.
So I write the macro like this:
(defmacro parser [clause]
(if (and (sequential? clause)
(= 2 (count clause))
(= `deref (first clause))
(string? (second clause)))
(let [s (second clause)
regx# #"#\{(.*?)\}"
m# (re-matcher regx# s)
p# (take-while #(not (nil? %)) (repeatedly #(second (re-find m#))))
ss# (clojure.string/replace s #"#\{(.*?)\}" "?" )
ps# (map symbol p#)]
`[~ss# ~#ps#])
clause))
This macro works well
user=> (let [aa 11] (parser #"AAA#{aa}" ))
["AAA?" 11]
user=> (let [aa 11] (parser #"AA{aa}" ))
["AA{aa}"]
But I want the macro $=> can transfer all of the pattern in the code , like:
(let [a 1 b 2 c 3]
($=> #"AAA#{a}"
(if true #"BBB#{b}")
(for [i (range 1)] #"CCC#{c}")))
and it can return
[["AAA?" 1] ["BBB?" 2] (["CCC?" 3])]
I have tried some method and all of them failed.
Now I have no idea how to solve this problem.

misunderstanding of variable arguments type

I am trying to solve a clojure problem where I implement my own comp function.
I have the following expression that works how I expect:
(reduce #(apply %2 [%1]) [1 2 3 4] [rest reverse])
This gives an output of
(4 3 2)
I have tried abstracting this into a function like this:
(((fn [& funcs]
(fn [& args]
(reduce #(apply %2 [%1]) args funcs)
)) rest reverse) [1 2 3 4])
But I get the following error when I run it:
CompilerException java.lang.ClassCastException: clojure.lang.ArraySeq
cannot be cast to java.lang.Number,
compiling:(/Users/paulcowan/projects/scratch/src/scratch/core.clj:1:1)
To me the only difference that I can see is how that funcs and args are different types than the vectors that I created in the first example.
Why does reduce and apply behave differently in the second example?
Simply:
(defn my-comp [& fns]
(fn [x] (reduce #(%2 %1) x fns)))
giving
((my-comp rest reverse) [1 2 3 4])
;(4 3 2)
As it should, my-comp returns an identity function for an empty argument list.
But it expects all functions, including the first applied,
to take a single argument.
To get round (2), adapt it as follows:
(defn my-comp [& fns]
(if (empty? fns)
identity
(let [[f & fs] fns]
(fn [& args] (reduce #(%2 %1) (apply f args) fs)))))
Apart from (1), this merely rephrases Mark's answer.
For fun ...
We could define my-comp in terms of the standard comp:
(defn my-comp [& fns] (apply comp (reverse fns)))
But it probably makes more sense the other way round, since comp has to reverse its argument list in general:
(defn comp [& fns] (apply my-comp (reverse fns)))
We could even define an argument reverser
(defn rev-args [f] (fn [& args] (apply f (reverse args))))
... which turns a function into one that does the same thing to a reversed argument list.
Then
(def comp (rev-args my-comp))
or vice-versa:
(def my-comp (rev-args comp))
First of all, I don't get any error messages (nor correct result):
user> (((fn [& funcs]
(fn [& args]
(reduce #(apply %2 [%1]) args funcs))) rest reverse) [1 2 3 4])
;; => ()
Difference between the two examples is that in the first one you pass value [1 2 3 4] into reduce, while in the second one you pass [[1 2 3 4]] (because args is meant to keep all arguments of the function as one vector.
This will work:
user> (((fn [& funcs]
(fn [args]
(reduce #(apply %2 [%1]) args funcs))) rest reverse) [1 2 3 4])
;; => (4 3 2)
However, to get a function for functional composition that will be able to take any number of arguments, you should write something like this:
user> (defn my-comp [& fncs]
(fn [& args]
(reduce #(%2 %1) ; you can omit apply here, as %2 is already function
; and %1 is always one value, as noisesmith noticed
(apply (first fncs) args)
(rest fncs))))
;; => #'user/my-comp
user> (def my-fnc (my-comp rest reverse))
;; => #'user/my-fnc
user> (my-fnc [1 2 3 4])
;; => (4 3 2)
It will work fine, because only first function should have ability to take many arguments, as others will be applied to value returned by previously called function.

Clojure's # lambda marco is not always the same as (fn)?

user> (map (fn [k] [k]) [1 2 3])
([1] [2] [3])
user> (map #([%1]) [1 2 3])
.... Error..
Why is the second example an error?
The #(<expr>) reader macro wraps the <expr> in an extra set of parenthesis, so #([%1]) expands to something equivalent to (fn [%1] ([%1])) and not (fn [%1] [%1]). So you are right. They are not entirely equivalent.
You can try the following in the REPL which will reveal the exact expansion:
user=> '#([%1])
(fn* [p1__862#] ([p1__862#]))
user=> '#(inc %1)
(fn* [p1__865#] (inc p1__865#))

Piping data through arbitrary functions in Clojure

I know that the -> form can be used to pass the results of one function result to another:
(f1 (f2 (f3 x)))
(-> x f3 f2 f1) ; equivalent to the line above
(taken from the excellent Clojure tutorial at ociweb)
However this form requires that you know the functions you want to use at design time. I'd like to do the same thing, but at run time with a list of arbitrary functions.
I've written this looping function that does it, but I have a feeling there's a better way:
(defn pipe [initialData, functions]
(loop [
frontFunc (first functions)
restFuncs (rest functions)
data initialData ]
(if frontFunc
(recur (first restFuncs) (rest restFuncs) (frontFunc data) )
data )
) )
What's the best way to go about this?
I must admit I'm really new to clojure and I might be missing the point here completely, but can't this just be done using comp and apply?
user> (defn fn1 [x] (+ 2 x))
user> (defn fn2 [x] (/ x 3))
user> (defn fn3 [x] (* 1.2 x))
user> (defn pipe [initial-data my-functions] ((apply comp my-functions) initial-data))
user> (pipe 2 [fn1 fn2 fn3])
2.8
You can do this with a plain old reduce:
(defn pipe [x fs] (reduce (fn [acc f] (f acc)) x fs))
That can be shortened to:
(defn pipe [x fs] (reduce #(%2 %1) x fs))
Used like this:
user> (pipe [1 2 3] [#(conj % 77) rest reverse (partial map inc) vec])
[78 4 3]
If functions is a sequence of functions, you can reduce it using comp to get a composed function. At a REPL:
user> (def functions (list #(* % 5) #(+ % 1) #(/ % 3)))
#'user/my-list
user> ((reduce comp functions) 9)
20
apply also works in this case because comp takes a variable number of arguments:
user> (def functions (list #(* % 5) #(+ % 1) #(/ % 3)))
#'user/my-list
user> ((apply comp functions) 9)
20