manuel@cui.UUCP (03/01/86)
I've often run into the problem of wanting to apply a function to every element of a list, but been stuck because the function takes more than one argument. As a simple example: (defun F (x y) (+ x y)) (setq L '(2 4 6 8)) How do we add 10 to each element of L? (mapcar 'F L '10) ; Error: bad list argument to map (mapcar 'F L '(10)) ; (12) (mapcar 'F L '(10 10 10 10)) ; (12 14 16 18) (mapcar '(lambda (x) (F x 10)) L) ; (12 14 16 18) The first example doesn't work for obvious reasons. The second returns a list whose length is the minimum of the argument lists which were given to mapcar. The third works correctly, but is cumbersome because we first had to know that the length of L was 4. The fourth is the best solution, but obscures the code because the number 10 has been hidden inside the lambda (this gets much worse for more complicated expressions). It would be better to have something with the form of the first or second examples. The following trick can be used to do this: (defun repeated (x) ; returns an 'infinite' list of x's ((lambda (list-x) (rplacd list-x list-x)) (list x))) (mapcar 'F L (repeated '10)) ; (12 14 16 18) The 'repeated' function returns a CONS cell whose CDR points to the cell itself. This results in an (apparant) infinite list of x's. Using this as an argument list to mapcar is clearer and doesn't depend on the lengths of the other argument lists. In general, it also costs less in space and in time. Hope this helps ... James Stewart University of Geneva UUCP: seismo!mcvax!cernvax!cui!manuel BITNET: stewart@cgeuge52
hucka@utah-cs.UUCP (Michael Hucka) (03/01/86)
In article <136@cui.UUCP> manuel@cui.UUCP (James Stewart) writes: > I've often run into the problem of wanting to apply a function to > every element of a list, but been stuck because the function takes > more than one argument. As a simple example: > > (defun F (x y) > (+ x y)) > > (setq L '(2 4 6 8)) > > How do we add 10 to each element of L? > > [examples and more discussion . . .] As an alternative, you could also do the following (assumine "L" and "F" are already defined): (let ((arg2 10)) (mapcar #'(lambda (arg1) (F arg1 arg2)) L)) Whether this works correctly may depend on the scoping aspects of the dialect of Lisp you are using. I believe this code is fairly transparent, though others may disagree with me . . . . Mike -- ........................................................................... :::::: Mike Hucka {ihnp4 decvax}!utah-cs!hucka, hucka@utah-cs.ARPA :::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
avolio@decuac.UUCP (03/08/86)
In <136@cui.UUCP>, manuel@cui.UUCP (MANUEL Alfred) writes: > I've often run into the problem of wanting to apply a function to > every element of a list, but been stuck because the function takes > more than one argument. As a simple example: > > (defun F (x y) > (+ x y)) > > (setq L '(2 4 6 8)) > > How do we add 10 to each element of L? > ... > The following trick can be used to do this: > > (defun repeated (x) ; returns an 'infinite' list of x's > ((lambda (list-x) > (rplacd list-x list-x)) > (list x))) > > (mapcar 'F L (repeated '10)) ; (12 14 16 18) I read this with a deep sense of deja vu, having done this 8 years ago or so. Please, understand this is not a put-down. By no means! I think that Manuel Alfred is quite clever, and I like to see thought put in to making code more elegant and into creating useful tools. I did want to pass on some things along the lines of what he is doing. A number of years ago some ideas along these lines were started by some ideas put down on paper in some Indiana University Computer Science Dept. Technical reports. Now, all of the reports I own have yet to be found (still boxed after a number of moves, I suspect). But what I am going to pass on here started with "CONS Should Not Evaluate Its Arguments" by Friedman and Wise and were implemented after a paper by the same professors regarding Functional Combination. In a distributed computing environment (I am only going to touch on this here because what I remember is from reading 8 years ago and I will get myself in deep trouble with the above mentioned authors, especially the Friedman part!) you want to be able to evaluate things in parallel. So you have a matrix like this: [ Fun1 Fun2 Fun3 ... FunN ] [ x1 x2 x3 ... xN ] [ y1 y2 y3 ... yN ] ... [ z1 z2 z3 ... zN ] where Fun1 is applied to x1 through z1, Fun2 to x2 through z2, etc. The result, usually, is a list of 'answers.' (Although in the paper, they intended that the result is the value of whichever finishes processing first -- again in a parallel computing environment.) So, we could have a function fc, which does these applications and be able to say: FC: [ sum length ] [ (1 2 3 4) (a b c d e f) ] and expect to get the answer (10 6), where 10 is the result of applying sum to (1 2 3 4) -- add the list elements -- and 6 is from applying length to (a b c d e f). Or, if you wanted to apply sum and length to the same argument, FC: [ sum length ] [ (1 2 3 4)* ] resulting in (10 4). The '*' function is Mr. Alfred's 'repeated' function, or 'star' (below). So we have two functions, we can use for the kinds of things Mr. Alfred suggests in many different ways. So, we have fc and star (I will include fchelp later). (def fc (lambda (funlist argmatrix) (fchelp funlist 'cons argmatrix))) (def star (lambda (xxx) ((lambda (lxxx) (rplacd lxxx lxxx)) (list xxx)))) So, (fc (list 'plus 'length) (star L)) (where L is (1 2 3 4)) returns (10 4). (By the way, fc was originally written as a FEXPR so that it could be called in a 'prettier' manner. I rewrote it since I have not used Lisp in quite a while and could not find FEXPRs. Maybe they are old-fashioned and I am dating myself... Note, below the function delete seems to be different than years back, and I had to write my own.) I have used fc extensively in many different programs. (Once you find a useful function you tend to use it as a tool, yes?) A few more interesting but unelaborate examples: Combinations of a set: (def combine (lambda (l) (cond ((null l) (list nil)) (t (append (combine* (car l) (cdr l)) (combine (cdr l))))))) (def combine* (lambda (a l) (fc (star 'cons) (list (star a) (combine l))))) So, (combine '(a b c)) returns ((a b c) (a b) (a c) (a) (b c) (b) (c) nil) Permutations of a set: (def permute (lambda (l) (cond ((null l) nil) ((null (cdr l)) (list l)) (t (fca (star 'mapcons) (list l (fc (star 'permute) (list (fc (star 'delete) (list l (star l))))))))))) produces: ((a b c) (a c b) (b a c) (b c a) (c a b) (c b a)) Oh... fca used above is just like fc but uses append instead of cons to put things together. Please note that the repeated or star function can be used with the function as well as the arguments. Also, note how fc is used differently. For example, it is used to take a list and produce copies of it -- each with one of the elements missing. (setq L '(a b c d e f g)) (fc (star 'delete) (list L (star L))) results in: ((b c d e f g) (a c d e f g) (a b d e f g) (a b c e f g) (a b c d f g) (a b c d e g) (a b c d e f)) Fun, huh? Additionally, if we are using functions that have differing numbers of arguments, we can still use the fc function. Example: (fc '(add1 plus) (list '(@ 3) '(5 9))) gives us (6 12). Note, '@' is used as a 'dead' element -- one to skip over. (This is why 'psucars' -- for pseudo-cars -- is used.) I am including all of the functions mentioned above at the end of this. At one time the idea of putting neat, fun, elegant, small, Lisp tools into a book was kicked around. The idea was to collect such tools (or Lispoems, because of the way they affect the 'reader' and their 'elegance' and 'flow' -- I wonder .. there may be just one other individual in the world who reads this and doesn't think I'm crazy!) into a small book. But, over the years it has been put terminally aside. (If anyone reading this thinks this sounds like a good idea, drop me a note.) Functions follow. (def fc (lambda (funlist argmatrix) (fchelp funlist 'cons argmatrix))) (def fca (lambda (funlist argmatrix) (fchelp funlist 'append argmatrix))) (def star (lambda (xxx) ((lambda (lxxx) (rplacd lxxx lxxx)) (list xxx)))) (def combine (lambda (l) (cond ((null l) (list nil)) (t (append (combine* (car l) (cdr l)) (combine (cdr l))))))) (def combine* (lambda (a l) (fc (star 'cons) (list (star a) (combine l))))) (def permute (lambda (l) (cond ((null l) nil) ((null (cdr l)) (list l)) (t (fca (star 'mapcons) (list l (fc (star 'permute) (list (fc (star 'delete) (list l (star l))))))))))) (def fchelp (lambda (funlist consfun argmat) (cond ((null funlist) nil) ((member nil argmat) nil) (t (apply consfun (list (apply (car funlist) (psucars argmat)) (fchelp (cdr funlist) consfun (mapcar (function cdr) argmat)))))))) (def psucars (lambda (l) (cond ((null l) nil) ((eq (caar l) '@) (psucars (cdr l))) (t (cons (caar l) (psucars (cdr l))))))) (def mapcons (lambda (x l) (fc (star 'cons) (list (star x) l)))) (def delete (lambda (a l) (cond ((null l) nil) ((eq a (car l)) (delete a (cdr l))) (t (cons (car l) (delete a (cdr l))))))) -- Fred @ DEC Ultrix Applications Center UUCP: {decvax,seismo,cbosgd}!decuac!avolio INET: avolio@decuac.DEC.COM