From: Samir Barjoud
Subject: Mapping by N elements
Date: 
Message-ID: <MPG.1173980e8bb1f8e9989681@news.mindspring.com>
Is there any function or macro defined by Common Lisp that does the 
following?

Example usage:
............
(map-by (x y) '(1 2 3 4 5 6)
	(+ x y))

=> (3 7 11)

(map-by (x y z) '(1 2 3 4 5 6)
	(* x y z))

=> (6 120)
............

Here is its fictional documentation:

(map-by VARLIST LIST &rest BODY)

A macro returning a list created by binding successive N elements of LIST 
to the symbols in VARLIST (where N is the length of VARLIST) and 
executing BODY each time around. The return value of BODY is used as each 
element of the return list.

.................
Here is my take at an implementation. Feel free to post improvements. 
One possible performance tweak would be to build the return list
in sequential order (advancing a tail pointer), avoiding the final call 
to NREVERSE.

(defmacro with-gensyms (symlist &rest body)
  `(let 
       (,@(mapcar #'(lambda (x)
		      `(,x (gensym)))
		  symlist))
     ,@body))

(defmacro map-by (varlist list &rest body)
  (let ((varindex -1))
    (with-gensyms (l times rlist)
      `(let* ((,l ,list)
	      (,times (/ (length ,l) ,(length varlist)))
	      ,rlist)
	 (symbol-macrolet
	     (,@(mapcar #'(lambda (x)
			    `(,x (nth ,(incf varindex) ,l)))
			varlist))
	   (loop repeat ,times do
		 (push ,@body ,rlist)
		 (setq ,l (nthcdr ,(length varlist) ,l)))
	   (nreverse ,rlist))))))

--
Samir Barjoud
·····@mindspring.com

From: Barry Margolin
Subject: Re: Mapping by N elements
Date: 
Message-ID: <VEoO2.301$kM2.42207@burlma1-snr2>
In article <··························@news.mindspring.com>,
Samir Barjoud <·····@mindspring.com> wrote:
>Is there any function or macro defined by Common Lisp that does the 
>following?

No.

>Here is my take at an implementation. Feel free to post improvements. 

(defmacro map-by (varlist list &rest body)
  (let ((skipfun `(lambda (l) (nthcdr ,(length varlist)))))
    `(loop for ,varlist on ,list by ,skipfun
           collect (progn ,@body))))

BTW, there's a bug in your code:

>		 (push ,@body ,rlist)

That PUSH won't work if BODY is more than one form.  Notice the PROGN in my
version.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Juanma Barranquero
Subject: Re: Mapping by N elements
Date: 
Message-ID: <370e1c27.26547222@news.mad.ttd.net>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Tue, 06 Apr 1999 14:14:13 GMT, Barry Margolin
<······@bbnplanet.com> wrote:

>BTW, there's a bug in your code:

BTW, there's a bug in your code:

>(defmacro map-by (varlist list &rest body)
>  (let ((skipfun `(lambda (l) (nthcdr ,(length varlist)))))

   (let ((skipfun `(lambda (l) (nthcdr ,(length varlist) l))))

>    `(loop for ,varlist on ,list by ,skipfun
>           collect (progn ,@body))))

                                                       /L/e/k/t/u

-----BEGIN PGP SIGNATURE-----
Version: PGPfreeware 6.0.2i

iQA+AwUBNwoPkf4C0a0jUw5YEQKyTACY4ZpHYb5/07vqFYr/e54Wc6OjkQCg9jhd
JStVb1a3dM0sVXR8yJ5I2bY=
=xUz4
-----END PGP SIGNATURE-----
From: Vassil Nikolov
Subject: Re: Mapping by N elements
Date: 
Message-ID: <7edtp1$jt0$1@nnrp1.dejanews.com>
Samir Barjoud wrote:
>
> Is there any function or macro defined by Common Lisp that does the
> following?
(...)
> (map-by (x y) '(1 2 3 4 5 6)
>         (+ x y))
>
> => (3 7 11)
(...)
> (map-by VARLIST LIST &rest BODY)
>
> A macro returning a list created by binding successive N elements of LIST
> to the symbols in VARLIST (where N is the length of VARLIST) and
> executing BODY each time around. The return value of BODY is used as each
> element of the return list.
(...)

Like the others whose replies I have seen, I, too, can't think of a
Common Lisp construct that does this directly (Barry Margolin's
suggestion, though, is pretty simple).  This looks like a generalisation
of MAPLIST.  Sort of:

  MAP-HEAD n-cdrs fn &REST lists+   [Function]

where n-cdrs is the number of arguments to be passed to fn.

(In fact, if fn is allowed to take a very restricted lambda list only,
containing only required parameters, the n-cdrs parameter would be
redundant, but that would clearly be undesirable as it would disallow
use of #'+ or #'* directly.)

I am curious: what specific problem do you want this for?  AFAIK, Lisp
experience shows MAPLIST is rarely needed.

In any case, I find it very uncomfortable to see this as a macro.  I think
it should be a function if it would be MAP-something, otherwise it ought
to be DO-something and not accumulate a list of results implicitly.  To
some extent this is personal taste, I agree.  But I think the rule that
macros should be used for control constructs, i.e. operations on _code_,
and not for operations on _data_ (with the exception of modifying places)
is valid for more important reasons than personal taste.

Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Samir Barjoud
Subject: Re: Mapping by N elements
Date: 
Message-ID: <MPG.1174ae5e3cfcc94e989682@news.mindspring.com>
Vassil Nikolov wrote:

> Like the others whose replies I have seen, I, too, can't think of a
> Common Lisp construct that does this directly (Barry Margolin's
> suggestion, though, is pretty simple).  This looks like a generalisation
> of MAPLIST.  Sort of:
> 
>   MAP-HEAD n-cdrs fn &REST lists+   [Function]
> 
> where n-cdrs is the number of arguments to be passed to fn.
> 
> (In fact, if fn is allowed to take a very restricted lambda list only,
> containing only required parameters, the n-cdrs parameter would be
> redundant, but that would clearly be undesirable as it would disallow
> use of #'+ or #'* directly.)
> 
> I am curious: what specific problem do you want this for?  AFAIK, Lisp
> experience shows MAPLIST is rarely needed.

   It was for an invented problem - which I create when I don't have any 
real ones to attack.  They always provide a learning opportunity, and 
sometimes become full-fledged programming projects.
   Here is one fabricated application of this type of mapping function.  
Consider a list of integers, which represent a digital audio sample.  
Each integer is the delta from a previous speaker position (which 
originates at position '0' at the start of the sample) to the current 
speaker position.  To double the pitch of the sample, replace every two 
consecutive integers with their sum.  To triple the pitch, replace every 
three with their sum. The length of the sample changes accordingly (1/2 
when doubling the pitch, 1/3 when tripling). (There are ways to adjust 
the pitch of a sample that preserve the length but are more 
computationally extensive and have nothing to do with this function).

 
> In any case, I find it very uncomfortable to see this as a macro.  I think
> it should be a function if it would be MAP-something, otherwise it ought
> to be DO-something and not accumulate a list of results implicitly.  To
> some extent this is personal taste, I agree.  But I think the rule that
> macros should be used for control constructs, i.e. operations on _code_,
> and not for operations on _data_ (with the exception of modifying places)
> is valid for more important reasons than personal taste.

  It could be a function - accepting a function as an argument as the 
mapping functions do.  But since there is no way to tell the number of 
arguments that a function is supposed to receive, it must be done either 
with a macro or as a function that is passed the number of elements to 
'map by'.

Here is MAP-BY defined as a function:

(defun map-by (n func list)
  (loop for l on list by #'(lambda (l) (nthcdr n l))
        collect (apply func (subseq l 0 n))))

(map-by 2 #'+ '(1 2 3 4 5 6))
=> (3 7 11)
(map-by 3 #'+ '(1 2 3 4 5 6))
=> (6 15)

In retrospect, this seems clearer and more powerfal than the macro form. 

> Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
> (You may want to cc your posting to me if I _have_ to see it.)
>    LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)
> 
> -----------== Posted via Deja News, The Discussion Network ==----------
> http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
> 

--
Samir Barjoud
·····@mindspring.com
From: Vassil Nikolov
Subject: Re: Mapping by N elements
Date: 
Message-ID: <7egdtm$li3$1@nnrp1.dejanews.com>
In article <··························@news.mindspring.com>,
  ·····@mindspring.com (Samir Barjoud) wrote:
(...)
>    Here is one fabricated application of this type of mapping function.
> Consider a list of integers, which represent a digital audio sample.
> Each integer is the delta from a previous speaker position (which
> originates at position '0' at the start of the sample) to the current
> speaker position.  To double the pitch of the sample, replace every two
> consecutive integers with their sum.
(...)

I see, thanks.  The only example I could think off the top of my head
was converting a p-list into an a-list, like

  (map-head 2 #'cons (symbol-plist 'foo))

but I don't think one would want to do this very often.  Your example
appears a better justification for a function like this one, though
I would prefer to think of the sample as a _stream_ of integeres,
and use e.g. series or something like that.  (One doesn't have to
have the whole sample in memory all at once---it could come from an
external source and the changed sample go into an external sink,
right?)

Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Samir Barjoud
Subject: Re: Mapping by N elements
Date: 
Message-ID: <MPG.1175a0dfcdcecf7f989683@news.mindspring.com>
Vassil Nikolov wrote:
(...)
> I would prefer to think of the sample as a _stream_ of integeres,
> and use e.g. series or something like that.  (One doesn't have to
> have the whole sample in memory all at once---it could come from an
> external source and the changed sample go into an external sink,
> right?)

Yes, it could.  I referred to this application as "fabricated" - which 
was a bad choice of words.  I meant to say that the application was "made 
up" or "fake", but "fabricated application" could be also be interpreted 
as "built application" or "actual code" - which is not what I intended.

> Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
> (You may want to cc your posting to me if I _have_ to see it.)
>    LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)
> 
> -----------== Posted via Deja News, The Discussion Network ==----------
> http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    

--
Samir Barjoud
·····@mindspring.com
From: Pierre R. Mai
Subject: Re: Mapping by N elements
Date: 
Message-ID: <87hfqs3uch.fsf@orion.dent.isdn.cs.tu-berlin.de>
Vassil Nikolov <········@poboxes.com> writes:

> I see, thanks.  The only example I could think off the top of my head
> was converting a p-list into an a-list, like
> 
>   (map-head 2 #'cons (symbol-plist 'foo))
> 
> but I don't think one would want to do this very often.  Your example

Not very often, but it sometimes does crop up, in a similiar guise,
when processing (parts of) keyword-value argument lists, and I have
indeed written a mapping-function for this (special) case, which does
mostly what map-head 2 does, but also includes a fair bit of
assertion-checking that the thing being mapped over is indeed a
valid arglist (which is why I didn't implement the more general
map-head)...

Just my 2 cents... (using this to recover from a serious amount of C
hacking on a PalmOS application ;)

-- 
Pierre Mai <····@acm.org>               http://home.pages.de/~trillian/
  "One smaller motivation which, in part, stems from altruism is Microsoft-
   bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
From: Erik Naggum
Subject: Re: Mapping by N elements
Date: 
Message-ID: <3132455299515355@naggum.no>
* ·····@mindspring.com (Samir Barjoud)
| Is there any function or macro defined by Common Lisp that does the
| following?
:
| Here is its fictional documentation:
| 
| (map-by VARLIST LIST &rest BODY)
| 
| A macro returning a list created by binding successive N elements of LIST 
| to the symbols in VARLIST (where N is the length of VARLIST) and 
| executing BODY each time around. The return value of BODY is used as each 
| element of the return list.

  the much undeservedly maligned extended LOOP macro does this for me
  whenever I need the above, which is only when processing property lists:

(loop
  for (key value) on <plist> by #'cddr
  collect (<whatever> key value))

  I don't see a need for a generalization of his idiom.

#:Erik
From: Vassil Nikolov
Subject: Re: Mapping by N elements
Date: 
Message-ID: <7egemc$mdn$1@nnrp1.dejanews.com>
In article <················@naggum.no> by Erik Naggum <····@naggum.no>
was written the following:

  (loop
    for (key value) on <plist> by #'cddr
    collect (<whatever> key value))

Can't help noting how, with the simple addition of four
punctuation characters (the angle brackets), this LOOP
form looks pretty nice.  Perhaps it would look even nicer
if "loop", "for", "on", etc. were in boldface.

Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own