unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* Collecting in the opposite order in a CL loop
@ 2010-02-27  1:35 Sean McAfee
  2010-02-27  2:42 ` Pascal J. Bourguignon
  0 siblings, 1 reply; 4+ messages in thread
From: Sean McAfee @ 2010-02-27  1:35 UTC (permalink / raw)
  To: help-gnu-emacs

Recently I composed this little function:

(defun digits-of (num)
  (assert (and (wholenump num) (not (zerop num))))
  (nreverse
   (loop for x = num then (/ x 10) until (zerop x) collect (mod x 10))))

It's short and sweet, but it bugs me just a little than I'm building up
a list only to immediately reverse it.  It seems to me that I ought to
be able to create the list already in the right order, but all I can
come up with so far (that uses the Common Lisp loop facility) is this:

(loop for x = num then (/ x 10) until (zerop x) with result = nil do
  (setq result (cons (mod x 10) result))
  finally return result)

That's substantially uglier than this routine that doesn't use a CL loop
at all:

(while (not (zerop x))
  (setq result (cons (mod x 10) result) x (/ x 10)))

...which I guess I could use, but I prefer to stick with the CL loop
macro when possible, if only because Emacs provides my only opportunity
to write any Common-Lisp(-like) code at all.

Is there an elegant way to build up a list "backwards" using the CL loop
facility?


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Collecting in the opposite order in a CL loop
  2010-02-27  1:35 Collecting in the opposite order in a CL loop Sean McAfee
@ 2010-02-27  2:42 ` Pascal J. Bourguignon
  2010-02-27  3:49   ` Sean McAfee
  0 siblings, 1 reply; 4+ messages in thread
From: Pascal J. Bourguignon @ 2010-02-27  2:42 UTC (permalink / raw)
  To: help-gnu-emacs

Sean McAfee <eefacm@gmail.com> writes:

> Recently I composed this little function:
>
> (defun digits-of (num)
>   (assert (and (wholenump num) (not (zerop num))))
>   (nreverse
>    (loop for x = num then (/ x 10) until (zerop x) collect (mod x 10))))
>
> It's short and sweet, but it bugs me just a little than I'm building up
> a list only to immediately reverse it.  It seems to me that I ought to
> be able to create the list already in the right order, but all I can
> come up with so far (that uses the Common Lisp loop facility) is this:
>
> (loop for x = num then (/ x 10) until (zerop x) with result = nil do
>   (setq result (cons (mod x 10) result))
>   finally return result)

Use push instead of setq cons.
Use truncate instead of / for integer division.
Use finally (return ...); finally return is ClTl2, not Common Lisp.

(loop
   with result = '()
   for x = num then (truncate x 10)
   until (zerop x) 
   do (push (mod x 10) result)
   finally (return result)))




> That's substantially uglier than this routine that doesn't use a CL loop
> at all:
>
> (while (not (zerop x))
>   (setq result (cons (mod x 10) result) x (/ x 10)))
>
> ...which I guess I could use, but I prefer to stick with the CL loop
> macro when possible, if only because Emacs provides my only opportunity
> to write any Common-Lisp(-like) code at all.
>
> Is there an elegant way to build up a list "backwards" using the CL loop
> facility?


Notice that building this list backwards as you want it is wrong:

1234 --> (1 2 3 4)
  34 --> (3 4)

with the most significant digits in the lowest indexes, you cannot use
the list of digits do to anything.



If you kept them in the right order, with least significant digits in
lowest indexes:

1234 --> (4 3 2 1)
  34 --> (4 3)

you can easily implement arithmetic operators, or comparisons, or
whatever you need to do with them.






(defun collect-digits (n)
   (unless (zerop n)
      (cons (mod n 10) (collect-digits (truncate n 10)))))

(collect-digits  1234) --> (4 3 2 1)
(collect-digits -1234) --> (6 7 8 9) ; 10-complement



(defun accumulate-digits (n d)
   (if (zerop n)
      d
      (accumulate-digits (truncate n 10) (cons (mod n 10) d))))

(accumulate-digits  1234 '()) --> (1 2 3 4)
(accumulate-digits -1234 '()) --> (9 8 7 6)  ; 10-complement


-- 
__Pascal Bourguignon__


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Collecting in the opposite order in a CL loop
  2010-02-27  2:42 ` Pascal J. Bourguignon
@ 2010-02-27  3:49   ` Sean McAfee
  2010-02-27 11:50     ` Pascal J. Bourguignon
  0 siblings, 1 reply; 4+ messages in thread
From: Sean McAfee @ 2010-02-27  3:49 UTC (permalink / raw)
  To: help-gnu-emacs

pjb@informatimago.com (Pascal J. Bourguignon) writes:

> Sean McAfee <eefacm@gmail.com> writes:
>> (loop for x = num then (/ x 10) until (zerop x) with result = nil do
>>   (setq result (cons (mod x 10) result))
>>   finally return result)

> Use push instead of setq cons.
> Use truncate instead of / for integer division.
> Use finally (return ...); finally return is ClTl2, not Common Lisp.

Thanks for the tips.  But what's the reason for the last two?  They seem
equivalent to me, aside from the latter forms having the small advantage
of brevity over the former.

>> Is there an elegant way to build up a list "backwards" using the CL loop
>> facility?

> Notice that building this list backwards as you want it is wrong:
>
> 1234 --> (1 2 3 4)
>   34 --> (3 4)
>
> with the most significant digits in the lowest indexes, you cannot use
> the list of digits do to anything.

Nothing except what I need it for.  I want to transform my input text in
chunks of a certain small unit, identified by a regexp, and to do so a
variable number of units at a time, inserting a space between the
groups.  The basic outline of my code is as follows:

(defun transform-text (arg)
  (interactive "p")
  (save-match-data
    (loop for count in (digits-of arg) do
      (loop repeat count do
        (search-forward-regexp "\\=\\s *\\(etc.etc.\\)")
        (replace-match (compute-replacement-text (match-string 1)))
        finally (insert " "))
      finally (backward-delete-char 1))))

So an argument of 123 to this routine would mean "transform one unit,
insert a space, transform two more units, add a space, and lastly
transform three more units."  I need the digits most-significant-first
because that's the order I typed them in.

Anyway, I like how the collect clause lets me accumulate a return value
for the loop without having to declare one explicitly, and was hoping a
similar construction might let me do the same in the correct order for
this usage.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Collecting in the opposite order in a CL loop
  2010-02-27  3:49   ` Sean McAfee
@ 2010-02-27 11:50     ` Pascal J. Bourguignon
  0 siblings, 0 replies; 4+ messages in thread
From: Pascal J. Bourguignon @ 2010-02-27 11:50 UTC (permalink / raw)
  To: help-gnu-emacs

Sean McAfee <eefacm@gmail.com> writes:

> pjb@informatimago.com (Pascal J. Bourguignon) writes:
>
>> Sean McAfee <eefacm@gmail.com> writes:
>>> (loop for x = num then (/ x 10) until (zerop x) with result = nil do
>>>   (setq result (cons (mod x 10) result))
>>>   finally return result)
>
>> Use push instead of setq cons.
>> Use truncate instead of / for integer division.
>> Use finally (return ...); finally return is ClTl2, not Common Lisp.
>
> Thanks for the tips.  But what's the reason for the last two?  They seem
> equivalent to me, aside from the latter forms having the small advantage
> of brevity over the former.

You expressed a desire to program in a more "common" lisp.
In CL (/ 13 10) --> 13/10,  and in CL finally return is invalid.



>>> Is there an elegant way to build up a list "backwards" using the CL loop
>>> facility?
>
>> Notice that building this list backwards as you want it is wrong:
>>
>> 1234 --> (1 2 3 4)
>>   34 --> (3 4)
>>
>> with the most significant digits in the lowest indexes, you cannot use
>> the list of digits do to anything.
>
> Nothing except what I need it for.  I want to transform my input text in
> chunks of a certain small unit, identified by a regexp, and to do so a
> variable number of units at a time, inserting a space between the
> groups.  The basic outline of my code is as follows:
>
> (defun transform-text (arg)
>   (interactive "p")
>   (save-match-data
>     (loop for count in (digits-of arg) do
>       (loop repeat count do
>         (search-forward-regexp "\\=\\s *\\(etc.etc.\\)")
>         (replace-match (compute-replacement-text (match-string 1)))
>         finally (insert " "))
>       finally (backward-delete-char 1))))
>
> So an argument of 123 to this routine would mean "transform one unit,
> insert a space, transform two more units, add a space, and lastly
> transform three more units."  I need the digits most-significant-first
> because that's the order I typed them in.
>
> Anyway, I like how the collect clause lets me accumulate a return value
> for the loop without having to declare one explicitly, and was hoping a
> similar construction might let me do the same in the correct order for
> this usage.

Well, the most elegant way is often to use a recursive function as I
showed.  Since it is expected that interactive user input won't overflow
the stack, there's no problem in using it.

-- 
__Pascal Bourguignon__


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2010-02-27 11:50 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-02-27  1:35 Collecting in the opposite order in a CL loop Sean McAfee
2010-02-27  2:42 ` Pascal J. Bourguignon
2010-02-27  3:49   ` Sean McAfee
2010-02-27 11:50     ` Pascal J. Bourguignon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).