I'm trying to find a way to do a general function of those down below, so you can put in a function f with arbitrary many inputs, and the program should do this embeded loop for each variable, and apply a given function on each loop.
(defun one (k1 f)
(funcall k1 (loop for x in domain collect (funcall f x))))
(defun two (k2 k1 f)
(funcall k2 (loop for y in domain collect
(funcall k1 (loop for x in domain collect
(funcall f y x))))))
(defun three (k3 k2 k1 f)
(funcall k3 (loop for z in domain collect
(funcall k2 (loop for y in domain collect
(funcall k1 (loop for x in domain collect
(funcall f z y x))))))
What I am trying to do is predicate logic in Lisp where kn would be the all och ex quantifier and f a formula with n amounts of free variables. Thank you for taking your time!
I tried to write such a macro which allows a variadic amount of k
s (N k
s):
(defmacro %predcall (f domain &rest ks)
(let* ((vars (loop for k in ks collect (gensym)))
(expr `(funcall ,f ,@(reverse vars))))
(loop for ki in ks
for var in vars
do (setf expr `(funcall ,ki (loop for ,var in ,domain collect ,expr)))
finally (return expr))))
Which you can test by:
(macroexpand-1 '(%predcall f domain k1))
;; (FUNCALL K1 (LOOP FOR #:G5665 IN DOMAIN COLLECT (FUNCALL F #:G5665))) ;
;; T
(macroexpand-1 '(%predcall f domain k1 k2))
;; (FUNCALL K2
;; (LOOP FOR #:G5667 IN DOMAIN COLLECT
;; (FUNCALL K1
;; (LOOP FOR #:G5666 IN DOMAIN COLLECT (FUNCALL F #:G5667 #:G5666))))) ;
;; T
(macroexpand-1 '(%predcall f domain k1 k2 k3))
;; (FUNCALL K3
;; (LOOP FOR #:G5670 IN DOMAIN COLLECT
;; (FUNCALL K2
;; (LOOP FOR #:G5669 IN DOMAIN COLLECT
;; (FUNCALL K1
;; (LOOP FOR #:G5668 IN DOMAIN COLLECT
;; (FUNCALL F #:G5670 #:G5669 #:G5668))))))) ;
;; T
gensym
solves the problem to name a variadic amount of variables in a unique way - so that they can't interefer with each other.
However, the order of the arguments in the call of this helper macro is not at all what you brought in your description of the problem.
So, we can use another macro then to get exactly the syntax/interface you want:
(defmacro predcall (&rest args)
(let* ((args-without-last (butlast args))
(ks (butlast args-without-last)) ;; args without last two
(f (car (last args-without-last))) ;; before last
(domain (car (last args)))) ;; the last (in args)
`(%predcall f domain ,@(reverse ks))))
Now this macro triggers the call of the helper macro with the "corrected" order of the arguments:
(macroexpand-1 '(predcall k1 f domain))
;; (%PREDCALL F DOMAIN K1) ;
;; T
(macroexpand-1 '(predcall k2 k1 f domain))
;; (%PREDCALL F DOMAIN K1 K2) ;
;; T
(macroexpand-1 '(predcall k3 k2 k1 f domain))
;; (%PREDCALL F DOMAIN K1 K2 K3) ;
;; T
To test, whether these calls will expand correctly as macros - you can write another macro which then calls the macroexpand-1 calls of the inner macro call. We call this macro to execute the macroexpand-1 calls - so we can prove to ourselves that the macro would do what it should:
(defmacro predcall-1 (&rest args)
"macroexpand-1 over the called function"
(let* ((args-without-last (butlast args))
(ks (butlast args-without-last))
(f (car (last args-without-last)))
(domain (car (last args))))
`(macroexpand-1 '(%predcall f domain ,@(reverse ks)))))
(predcall-1 k1 f domain)
;; (FUNCALL K1 (LOOP FOR #:G5675 IN DOMAIN COLLECT (FUNCALL F #:G5675))) ;
;; T
(predcall-1 k2 k1 f domain)
;; (FUNCALL K2
;; (LOOP FOR #:G5677 IN DOMAIN COLLECT
;; (FUNCALL K1
;; (LOOP FOR #:G5676 IN DOMAIN COLLECT (FUNCALL F #:G5677 #:G5676))))) ;
;; T
(predcall-1 k3 k2 k1 f domain)
;; (FUNCALL K3
;; (LOOP FOR #:G5680 IN DOMAIN COLLECT
;; (FUNCALL K2
;; (LOOP FOR #:G5679 IN DOMAIN COLLECT
;; (FUNCALL K1
;; (LOOP FOR #:G5678 IN DOMAIN COLLECT
;; (FUNCALL F #:G5680 #:G5679 #:G5678))))))) ;
;; T
So you can see, you can enter the arguments in the order you want.
(The only difference is that domain
is entered as last argument
while you prefer to capture domain as a closure).
So finally, we can be sure that we can call the macro in this form:
(predcall kn ... k3 k2 k1 f domain)
Isn't it amazing, with how relatively concise three (actually two) macros you can achieve all this in Common Lisp?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With