Macro to construct function names dynamically

Discussion of Common Lisp
Post Reply
foregam
Posts: 4
Joined: Thu Jul 28, 2011 6:05 am

Macro to construct function names dynamically

Post by foregam » Thu Jul 28, 2011 6:49 am

Hello,

I'm new to Lisp and my previous programming experience was mostly C, Perl and shell scripts. I'm still heavily influenced by it so please bear with me.
So: Is there a way to define a macro which constructs a function name when given a list of symbols? I'd like to call it like this:

Code: Select all

(do-expand-functions '(first second third fourth fifth) '(a b c) (list foo bar baz quux))
and get as a result

Code: Select all

(progn
  (do-function-a-first foo)
  (do-function-a-second foo)
  (do-function-a-third foo)
  (do-function-a-fourth foo)
  (do-function-a-fifth foo)
  (do-function-b-first bar)
  (do-function-b-second bar)
  .
  .
  .
  (do-function-c-fifth quux))
How do I construct the do-function-* names? Once I get past that the rest is easy. Is there a common idiom for this kind of things? If I were using Bash I'd probably say

Code: Select all

for I in a b c; do
    for J in first second third; do
        for K in foo bar baz quux; do
            eval do_function_${I}_${J} $K
        done
    done
done
although it's not quite the same.

Thank you in advance.

nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: Macro to construct function names dynamically

Post by nuntius » Thu Jul 28, 2011 10:34 am

Here are a few tools at your disposal:

Code: Select all

(symbol-name 'a) ; "A"
(string 'A); "A"
(format nil "~{~A~}" '(a b c)) ; "ABC"
(intern "C") ; 'C
(make-symbol "D") ; '#:D
The first three lines show ways of getting strings from symbols; the second show the opposite transform. There are several things to be careful of here; you might want to call these inside with-standard-io-syntax, bind *print-case*, etc. There are a couple existing functions to do this but none have widespread popularity.

Part of the problem is that people can customize the CL reader and printer. Part of the problem is getting the symbols in the right package. In the end, some experienced users don't like machine-generated names. They prefer APIs which allow you to specify the name for everything (see for example defstruct vs defclass). Also, it is often better to use generic functions or a data structure (e.g. plist, hashtable, or array) than constructed names.

Kompottkin
Posts: 94
Joined: Mon Jul 21, 2008 7:26 am
Location: München, Germany
Contact:

Re: Macro to construct function names dynamically

Post by Kompottkin » Fri Jul 29, 2011 12:41 am

foregam wrote:

Code: Select all

(do-expand-functions '(first second third fourth fifth) '(a b c) (list foo bar baz quux))
Since it looks like you want the arguments to be evaluated at run-time, you may want a function rather than a macro.

In that case, you can do it basically the same way as in Bash, except without using eval. In addition to the functions that nuntius has referenced, funcall is your friend.

foregam
Posts: 4
Joined: Thu Jul 28, 2011 6:05 am

Re: Macro to construct function names dynamically

Post by foregam » Fri Jul 29, 2011 4:46 am

Thank you both very much for your concise answers. @Kompottkin, no, I actually want it as a shorthand for writing a big number of similarly looking lines by hand, which I consider a very likely source of errors. Maybe I should refactor my source further on. The Bash example I gave was simplified to make my general intent clear.

adam33147
Posts: 20
Joined: Sat Aug 20, 2011 6:49 pm

Re: Macro to construct function names dynamically

Post by adam33147 » Tue Aug 30, 2011 7:18 pm

Here is a version of a macro which creates function calls according to every possable combination of concatination of the symbols in symbol-list as the functions, and every possable concatination of the args. If an atom is applied anywhere, it is included in its place according to the pattern.

Code: Select all

(defmacro funcall-matrix (symbol-list args-list)
	   (let ((functions (apply #'symbol-matrix symbol-list))
		 (args (apply #'map-each-to-every args-list)))
	     (cons 'list
		   (mapcar #'(lambda (elt)
			       (cons (car elt) (cadr elt)))
			   (map-each-to-every functions args)))))

(defun symbol-matrix (&rest symbols)
	   (mapcar 
	  #'(lambda (symbol-list)
	      (apply #'symbol-concat symbol-list))
	  (apply #'map-each-to-every symbols)))

(defun map-each-to-every (&rest atoms-or-lsts)
	   (labels ((mp (&rest lsts)
		      (if (cdr lsts)
			  (apply #'mp
				 (cons 
				  (apply #'append
					 (mapcar #'(lambda (each-elt)
						     (mapcar #'(lambda (every-elt)
								 (append each-elt (list every-elt)))
							     (ensure-list (second lsts))))
						 (ensure-list (first lsts))))
				  (cddr lsts)))
			  (first lsts))))
	     (apply #'mp 
		    (cons (mapcar #'list (ensure-list (car atoms-or-lsts)))
			  (cdr atoms-or-lsts)))))

(defun symbol-concat (&rest symbols)
	   (intern
	    (apply #'string-concat
		   (mapcar 
		    #'(lambda (elt)
			(if (symbolp elt)
			    (symbol-name elt)
			    (write-to-string elt)))
		    symbols))))

(defun ensure-list (elt)
	   (if (listp elt)
	       elt
	       (list elt)))

For example, calling
>(macroexpand '(funcall-matrix ((a b c) - (1 2 3 4)) ((arg-a arg-b) 15 (arg-x arg-y arg-z))))

(LIST (A-1 ARG-A 15 ARG-X) (A-1 ARG-A 15 ARG-Y) (A-1 ARG-A 15 ARG-Z)
(A-1 ARG-B 15 ARG-X) (A-1 ARG-B 15 ARG-Y) (A-1 ARG-B 15 ARG-Z) (A-2 ARG-A 15 ARG-X)
(A-2 ARG-A 15 ARG-Y) (A-2 ARG-A 15 ARG-Z) (A-2 ARG-B 15 ARG-X) (A-2 ARG-B 15 ARG-Y)
(A-2 ARG-B 15 ARG-Z) (A-3 ARG-A 15 ARG-X) (A-3 ARG-A 15 ARG-Y) (A-3 ARG-A 15 ARG-Z)
(A-3 ARG-B 15 ARG-X) (A-3 ARG-B 15 ARG-Y) (A-3 ARG-B 15 ARG-Z) (A-4 ARG-A 15 ARG-X)
(A-4 ARG-A 15 ARG-Y) (A-4 ARG-A 15 ARG-Z) (A-4 ARG-B 15 ARG-X) (A-4 ARG-B 15 ARG-Y)
(A-4 ARG-B 15 ARG-Z) (B-1 ARG-A 15 ARG-X) (B-1 ARG-A 15 ARG-Y) (B-1 ARG-A 15 ARG-Z)
(B-1 ARG-B 15 ARG-X) (B-1 ARG-B 15 ARG-Y) (B-1 ARG-B 15 ARG-Z) (B-2 ARG-A 15 ARG-X)
(B-2 ARG-A 15 ARG-Y) (B-2 ARG-A 15 ARG-Z) (B-2 ARG-B 15 ARG-X) (B-2 ARG-B 15 ARG-Y)
(B-2 ARG-B 15 ARG-Z) (B-3 ARG-A 15 ARG-X) (B-3 ARG-A 15 ARG-Y) (B-3 ARG-A 15 ARG-Z)
(B-3 ARG-B 15 ARG-X) (B-3 ARG-B 15 ARG-Y) (B-3 ARG-B 15 ARG-Z) (B-4 ARG-A 15 ARG-X)
(B-4 ARG-A 15 ARG-Y) (B-4 ARG-A 15 ARG-Z) (B-4 ARG-B 15 ARG-X) (B-4 ARG-B 15 ARG-Y)
(B-4 ARG-B 15 ARG-Z) (C-1 ARG-A 15 ARG-X) (C-1 ARG-A 15 ARG-Y) (C-1 ARG-A 15 ARG-Z)
(C-1 ARG-B 15 ARG-X) (C-1 ARG-B 15 ARG-Y) (C-1 ARG-B 15 ARG-Z) (C-2 ARG-A 15 ARG-X)
(C-2 ARG-A 15 ARG-Y) (C-2 ARG-A 15 ARG-Z) (C-2 ARG-B 15 ARG-X) (C-2 ARG-B 15 ARG-Y)
(C-2 ARG-B 15 ARG-Z) (C-3 ARG-A 15 ARG-X) (C-3 ARG-A 15 ARG-Y) (C-3 ARG-A 15 ARG-Z)
(C-3 ARG-B 15 ARG-X) (C-3 ARG-B 15 ARG-Y) (C-3 ARG-B 15 ARG-Z) (C-4 ARG-A 15 ARG-X)
(C-4 ARG-A 15 ARG-Y) (C-4 ARG-A 15 ARG-Z) (C-4 ARG-B 15 ARG-X) (C-4 ARG-B 15 ARG-Y)
(C-4 ARG-B 15 ARG-Z))

I remeber when I started programming in Lisp, I was comming from Java and implemented a simple OO system. Its funny how influneced you are by the languages you use.

Post Reply