I have come up with the below function, which works as intended but it uses eval which is horrible, and does not exist in ClojureScript where i intend to use it.
(defn path [d p]
(eval
(concat '[-> d]
(flatten (map
#(conj (repeat (dec %) 'z/right) 'z/down)
(path-to-vector p))))))
How would I convert it to a macro? My attempt looks like this:
(defmacro path [d p]
`(concat (-> ~d)
(flatten
(map #(conj (repeat (dec %) z/right) z/down)
(path-to-vector ~p)))))
But that clearly does not work.
No need for a macro or eval, the operation is just a reduce:
(defn path [d p]
(reduce (fn [s v]
(reduce #(%2 %1) s (conj (repeat (dec v) z/right) z/down)))
d (path-to-vector p)))
Also note that (conj (repeat (dec %) z/right) z/down) means z/down and then all the z/right coz repeate return a sequence, in case you want all z/right first and last item should be z/down then you should use (conj (vec (repeat (dec %)) z/right) z/down)
Ankur is correct that this is a case for reduce and neither a macro or eval is appropriate, though it's perhaps still worth explaining the mechanics of writing a macro such as this for it's own sake:
Your macro example is very close, all you need is the splicing-unquote function to make it work:
(defmacro path [d p]
`(-> ~d
~#(flatten
(map #(conj (repeat (dec %) z/right) z/down)
(path-to-vector ~p)))))
this evaluates the call to flatten at macro expansion time and then concatenates it into the resulting s-expression/list.
Related
I am trying to expand all macros inside a nested list structure.
macroexpand-all almost works, but skips (does not expand) the first form in every nested list.
I am using this as a template-mechanism for org-agenda-custom-commands. I can generate agenda-blocks for multiple agenda-commands via macros. This is in init.el (emacs26.2).
macroexp-all-forms is able to not skip the first form, but calls macroexpand-all for nested forms.
Here a minimal example from the emacs doc:
(defmacro inc (var)
(list 'setq var (list '1+ var)))
This works as expected (one macro-call):
ELISP> (macroexpand-all '(inc r))
(setq r
(1+ r))
This works too (nested, but first form is not a macro-call):
ELISP> (macroexpand-all '(('foo)(inc r)))
(('foo)
(setq r
(1+ r)))
This does NOT work (nested and first form is a macro-call):
ELISP> (macroexpand-all '((inc r)(inc r)))
((inc r)
(setq r
(1+ r)))
This also does not work:
ELISP> (macroexpand-all '((inc r)))
((inc r))
In the last two examples, the first call to inc is not expanded. What am I missing here?
How can i really expand all macros in this situation?
macroexpand-all expects a form as the argument, but ((inc r) (inc r)) is not a form. A form should be something that can be evaluated. In the case of a list, that means the first element must be a name of a function, macro or a special operator, or a lambda expression.
The special operator progn can be used for a list of forms to be evaluated sequentially. For example:
(macroexpand-all '(progn (inc r) (inc r)))
;=> (progn
; (setq r
; (1+ r))
; (setq r
; (1+ r)))
Alternatively, if the list is not meant to be a form, you can use mapcar to apply macroexpand-all to each member of the list. For example:
(mapcar #'macroexpand-all '((inc r) (inc r)))
;=> ((setq r
; (1+ r))
; (setq r
; (1+ r)))
Keep in mind that the result here is a list of forms, but not a form itself; it cannot be evaluated as is, but the individual elements can be.
The form you want to expand should make sense as a form for evaluation. ((inc r)) does not: it can't be legal elisp.
I'm actually fairly surprised that macroexpand-all doesn't raise an error in the case you've given it. If you want it to work you need to fake up something which is potentially legal. For instance
ELISP> (macroexpand-all '(grut (inc r) (inc r)))
(grut
(setq r
(1+ r))
(setq r
(1+ r)))
ELISP> (cdr (macroexpand-all '(grut (inc r) (inc r))))
((setq r
(1+ r))
(setq r
(1+ r)))
grut does not need to be defined: it only needs not to be a macro so macroexpand-all just ignores it. (In fact, using progn as jkiiski suggests is a better answer).
As an aside, it sounds like you are using macroexpansion to do something other than expand Lisp code: that's almost always a bad idea. It's much better to write your own expander which understands the rules of the language you are implementing in Lisp.
I was looking for a way to repeat a function call a certain number of times and accumulate the results in a list and couldn't find anything in the standard library, so I wrote one. It's simple to write, but it seems like such an obvious thing that I feel like there must be an accepted way of doing this using standard library functions.
Here are the two functions I'm looking to replace:
(define (repeat n f)
(unless (<= n 0)
(f)
(repeat (sub1 n) f)))
(define (accumulate n f)
(let loop ([n n] [l empty])
(if (<= n 0)
l
(loop (sub1 n)
(cons (f) l)))))
Is there any simpler way of achieving this?
If your function does not any take arguments, you can use build-list
Example:
#lang racket
;; The function you want to call many times
(define (f)
#t)
;; Use build list with a lambda to wrap your function because
;; build-list want a function takin integer as its second argument
(build-list 5 (lambda (x) (f)))
result:
'(#t #t #t #t #t)
Edit: you can also define a function to wrap the lambda
(define (repeat-then-accumulate n f)
(build-list n (lambda (x) (f)))
)
Usage:
;; Using the f function defined earlier
(repeat-then-accumulate 10 f)
result:
'(#t #t #t #t #t #t #t #t #t #t)
Edit2: If you want to have fixed args, you could do something like
#lang racket
;; The function you want to call many times
(define (f a b)
(+ a b))
(define (repeat-then-accumulate n f args)
(build-list n (lambda (x) (apply f args)))
)
Usage:
(repeat-then-accumulate 10 f '(3 5))
Result:
'(8 8 8 8 8 8 8 8 8 8)
It looks like #AlexKnauth didn't feel like taking your internet points for his answer, and he phrased it as a comment. I'm not proud, though... Use racket's list comprehension form:
(for/list ([i (in-range n)]) (f i))
(I added an explicit "in-range", just to get better error-checking.)
In my answer to Clojure For Comprehension example, there is some duplication that I would like to remove:
(def all-letters (map char (range 65 90)))
(defn kw [& args] (keyword (apply str args)))
(concat
(for [l all-letters] (kw l))
(for [l all-letters l2 all-letters] (kw l l2))
(for [l all-letters l2 all-letters l3 all-letters] (kw l l2 l3)))
I would like to remove the "for" duplication. I have written the following macro:
(defmacro combine [times]
(let [symbols (repeatedly times gensym)
for-params (vec (interleave symbols (repeat 'all-letters)))]
`(for ~for-params (kw ~#symbols))))
Which works with:
(concat (combine 1) (combine 2) (combine 3))
But if I try:
(for [i (range 1 4)] (combine i))
I get:
CompilerException java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to java.lang.Number, compiling:(NO_SOURCE_PATH:177)
What is going on? Is there a better way of removing the duplication?
Your problem is that combine is a macro that gets expanded at compile time. When you try to expand on a symbol i it fails, because it is designed to take a number (times). i is just a symbol at compile time, it only evaluates to numeric values at runtime.
I'd suggest rewriting combine to be a function rather than a macro: you don't really need macros here and functions are frequently more convenient (as in this case!).
Here's a recursive combine that probably does roughly what you want:
(defn combine
([times] (combine times nil))
([times letters]
(if (<= times 0)
(keyword (apply str letters))
(flatten (for [l all-letters] (combine (dec times) (cons l letters)))))))
You can modify your macro such that the concat becomes part of the macro, such shown below. But I agree with Mikera that it is better to have a function.
(defmacro combine [start end]
`(concat
~#(for [i (range start end)]
(let [symbols (repeatedly i gensym)
for-params (vec (interleave symbols (repeat 'all-letters)))]
`(for ~for-params (kw ~#symbols))))))
user=> (combine 1 2)
(:A :B :C :D :E :F :G :H :I :J :K :L :M :N :O :P :Q :R :S :T :U :V :W :X :Y)
I'm currently working on a LISP exercise for a small project and need severe help. This may be more or less of a beginner's question but I'm absolutely lost on writing a certain function that takes in two unevaluated functions and spits out the result dependent on if the variables were given an assignment or not.
An example would be
(setq p1 '(+ x (* x (- y (/ z 2)))))
Where
(evalexp p1 '( (x 2) (z 8) ))
returns (+ 2 (* 2 (- y 4)))
My goal is to write the evalexp function but I can't even think of where to start.
So far I have
(defun evalexp (e b) )
.. not very much. If anyone could please help or lead me in a good direction I'd be more than appreciative.
Here's a full solution. It's pretty straightforward, so I'll leave out a full explanation. Ask me in the comments if there's anything you can't figure out yourself.
(Using eval to do the actual evaluation might not be what you want in your exercise/project. Look up "meta-circular interpreter" for another way.)
(defun apply-env (exp env)
(reduce (lambda (exp bdg) (subst (cadr bdg) (car bdg) exp))
env :initial-value exp))
(defun try-eval (exp)
(if (atom exp)
exp
(let ((exp (mapcar #'try-eval exp)))
(if (every #'numberp (cdr exp))
(eval exp)
exp))))
(defun evalexp (exp env)
(try-eval (apply-env exp env)))
Here's a hint, this is how you might do it (in pseudocode):
function replace(vars, list):
for each element of list:
if it's an atom:
if there's an association in vars:
replace atom with value in vars
else:
leave atom alone
else:
recursively apply replace to the sublist
There will certainly be some details to work out as you convert this to Lisp code.
I'm trying (as a self-learning exercise) to create a Clojure macro that will generate code to apply a function to a sequence of integers and sum the result, e.g.
f(0) + f(1) + f(2) + f(3)
This is my attempt:
(defmacro testsum [func n]
`(fn [x#] (+ ~#( map (fn [i] `(~func x#)) (range n)))))
However something seems to go wrong with the x# gensym and I end up with two different versions of x and hence the function doesn't work:
(macroexpand '(testsum inc 3))
gives:
(fn* ([x__809__auto__]
(clojure.core/+
(inc x__808__auto__)
(inc x__808__auto__)
(inc x__808__auto__))))
This is pretty much exactly what I want apart from the different 809 and 808 versions of x.....
What am I doing wrong? I thought that the auto gensym was meant to create a single unique symbol for exactly this kind of purpose? Is there a better way of doing this?
foo#-style gensyms are valid only inside the syntax-quote where they were created. In your code, the two x#s are created in different syntax-quote blocks:
(defmacro testsum [func n]
`(fn [x#] (+ ~#( map (fn [i] `(~func x#)) (range n)))))
^- s-q1 ^-unquote ^- s-q2
To fix this, use an explicit (gensym) call:
(defmacro testsum [func n]
(let [x (gensym "x")]
`(fn [~x] (+ ~#(map (fn [i] `(~func ~x)) (range n))))))
And the macro expansion ((macroexpand '(testsum inc 3))):
(fn* ([x4966] (clojure.core/+ (inc x4966) (inc x4966) (inc x4966))))