unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Generator Examples
@ 2016-09-22  0:47 raman
  2016-09-25  0:16 ` Michael Heerdegen
  0 siblings, 1 reply; 7+ messages in thread
From: raman @ 2016-09-22  0:47 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: message body text --]
[-- Type: text/plain, Size: 380 bytes --]

I'm attaching a short texinfo file containing a couple of examples of
using generators in Emacs Lisp.

The texinfo manual contains one example which I found confusing in
parts:

1. The example has  a nested call to iter-yield -- not sure why
2. I still dont quite understand the purpose of the optional argument
to iter-next, an example that leverages  it would be good to have.


[-- Attachment #2: Generator Examples in texinfo --]
[-- Type: application/octet-stream, Size: 1554 bytes --]

\input texinfo    @c -*- texinfo -*-
@c %**start of header
@setfilename ./generators.info
@settitle Generators In Emacs Lisp
@documentencoding UTF-8
@documentlanguage en
@c %**end of header

@finalout
@titlepage
@title Generators In Emacs Lisp
@author raman
@end titlepage

@ifnottex
@node Top
@top Generators In Emacs Lisp
@end ifnottex

@menu
* Define a Fibonacci generator::
* Iterate Over A List:: Iterate Over A List. 
@end menu

@node Define a Fibonacci generator
@chapter Define a Fibonacci generator

@lisp
(iter-defun fibonacci-iter ()
  "Return a Fibonacci sequence generator."
  (let ((a 1)
        (b 1))
    (while t
      (iter-yield a)
      (cl-psetq a b b (+ a b )))))
@end lisp

Instantiate a Fibonacci generator and use it to collect the first 10
numbers in the sequence.

@lisp
(setq f (fibonacci-iter))
;;; The first 10 Fibonacci numbers:
(cl-loop for i from 1 to 10 collect (iter-next f))
@end lisp

@node Iterate Over A List
@chapter Iterate Over A List:

A generator that takes a list and returns an iterator over that list:

@lisp
(iter-defun list-iter (l)
  "Return an iterator that iterates over list `l'."
  (let ((local(copy-sequence l)))
    (while local (iter-yield (pop local)))))
@end lisp


Test the above by iterating over a list. This example is mostly for
illustration  demonstrates the use of @code{cl-loop} clause @code{iter-by}.

@lisp
;;; Create a list iterator:

(setq l-iter (list-iter '(a b c d e)))
;;; Loop  through the iterator, collecting values:
(cl-loop for e iter-by   l-iter collect e)
@end lisp

@bye

[-- Attachment #3: .signature --]
[-- Type: text/plain, Size: 5 bytes --]


-- 

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

* Re: Generator Examples
  2016-09-22  0:47 Generator Examples raman
@ 2016-09-25  0:16 ` Michael Heerdegen
  2016-09-25  0:29   ` raman
  2016-09-25  0:32   ` raman
  0 siblings, 2 replies; 7+ messages in thread
From: Michael Heerdegen @ 2016-09-25  0:16 UTC (permalink / raw)
  To: raman; +Cc: emacs-devel

Hey raman,

> I'm attaching a short texinfo file containing a couple of examples of
> using generators in Emacs Lisp.

Great.  This would be an improvement for the manual indeed.

> The texinfo manual contains one example which I found confusing in
> parts:

Yeah, the current doc is more for people already familiar with the
concept.

> 1. The example has a nested call to iter-yield -- not sure why

Just to demonstrate how it behaves, I think.  It's probably not very
common/useful in practise.

> 2. I still dont quite understand the purpose of the optional argument
> to iter-next, an example that leverages it would be good to have.

I think it's more useful if you think of generators as coroutines.  A
simple real life example would be: you have implemented a counter as a
generator (generating 1, 2, ...), and when you restart it, you want to
add a certain increment to its count so far before continuing.

> @lisp
> (iter-defun fibonacci-iter ()
>   "Return a Fibonacci sequence generator."
>   (let ((a 1)
>         (b 1))
>     (while t
>       (iter-yield a)
>       (cl-psetq a b b (+ a b )))))
> @end lisp
 
Looks good (but could we get rid of the a bit uncommon `cl-psetq').  I
remember that the suggested convention was to call the iter-defun a
generator, and the return value an iterator (in this regard, the
docstring would be wrong).

FWIW, some time ago a had posted a similar example, implementing a
generator of the prime numbers:


(iter-defun cross-through-multiples-of (n)
  "Repeat indefinitely: Return `t' N-1 times, then return `nil' once."
  (let ((i (1- n)))
    (while t
      (if (zerop i)  (progn (setq i (1- n)) (iter-yield nil))
        (iter-yield t)
        (cl-decf i)))))
   
(iter-defun make-prime-gen ()
  "Return a generator of the prime numbers."
  (let ((n 2) (sieve '()))
    (while t
      (when (cl-every #'identity (mapcar #'iter-next sieve))
        ;; a new prime!
        (push (cross-through-multiples-of n) sieve)
        (iter-yield n))
      (cl-incf n))))


> A generator that takes a list and returns an iterator over that list:
> 
> @lisp
> (iter-defun list-iter (l)
>   "Return an iterator that iterates over list `l'."
>   (let ((local(copy-sequence l)))
>     (while local (iter-yield (pop local)))))
> @end lisp

Isn't the `copy-sequence' redundant (popping the local variable doesn't
alter the original list)?

> @lisp
> ;;; Create a list iterator:
> 
> (setq l-iter (list-iter '(a b c d e)))
> ;;; Loop  through the iterator, collecting values:
> (cl-loop for e iter-by   l-iter collect e)
> @end lisp
> 
> @bye


To improve the doc even further, I think it would be good if we could as
well:

- Underline the difference between generators and its "instances"
(iterators), and that multiple iterators of the same "type" have
independent inner states.

- Have an example that doesn't create lists at the end, to underline
that it is a different concept, and/or

- Have a real life example (something like tree traversal, e.g. a
generator of directory files or so) that is an improvement compared to
using list.


Regards,

Michael.



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

* Re: Generator Examples
  2016-09-25  0:16 ` Michael Heerdegen
@ 2016-09-25  0:29   ` raman
  2016-09-25  1:03     ` Michael Heerdegen
  2016-09-25  0:32   ` raman
  1 sibling, 1 reply; 7+ messages in thread
From: raman @ 2016-09-25  0:29 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

I'm familiar with generators as a concept across multiple languages --
but I still found the  elisp docs confusing:-)

We explicitly say the interface is similar to that in languages like
Python -- which is why i coded up the Fibonacci example.

The cl-psetq is the right idiom here in that sense -- if you use plain
setq, you'd have to use a temporary variable?

Thanks for the coroutine example, again that would do well in the
manual -- there is a reference to coroutines either in the manual or in
code comments in generators.el that is presently "intriguing".

Finally, I  still dont understand the nested call to iter-yield in the
manual and  if it is correct, it would be good to have it better
explained there.


-- 



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

* Re: Generator Examples
  2016-09-25  0:16 ` Michael Heerdegen
  2016-09-25  0:29   ` raman
@ 2016-09-25  0:32   ` raman
  2016-09-25  1:06     ` Michael Heerdegen
  1 sibling, 1 reply; 7+ messages in thread
From: raman @ 2016-09-25  0:32 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

P.S. In my  quick tests, the copy-sequence was necessary --  
If it weren't generators would get inefficient quickly because you'd
have to do a deep copy of the variable you were snapshotting in the closure.
-- 



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

* Re: Generator Examples
  2016-09-25  0:29   ` raman
@ 2016-09-25  1:03     ` Michael Heerdegen
  2016-09-25  2:26       ` T.V Raman
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Heerdegen @ 2016-09-25  1:03 UTC (permalink / raw)
  To: raman; +Cc: emacs-devel

raman <raman@google.com> writes:

> The cl-psetq is the right idiom here in that sense -- if you use plain
> setq, you'd have to use a temporary variable?

Sure, but I want to avoid to confront people with functions they
potentially don't know in these examples, so that they can concentrate
on the gist.  A minor issue, sure.

> Thanks for the coroutine example, again that would do well in the
> manual -- there is a reference to coroutines either in the manual or
> in code comments in generators.el that is presently "intriguing".

AFAIK the original author wanted to add some code for working with
coroutines.

> Finally, I still dont understand the nested call to iter-yield in the
> manual and if it is correct, it would be good to have it better
> explained there.

I think it is correct (behaves exactly as described, and the behavior is
what I expect).  What in particular don't you understand?

I think the thing that this part wants to demonstrate is that
`iter-yield' behaves normally wrt when it is evaluated (function
arguments before function calls etc.; so the inner `iter-yield' is
called first, and the outer not before the iterator has been restarted).
And the outer `iter-yield' yields a value that is computed from the
"return value" of the inner, which is the second argument of the
according `iter-next' call.


Michael.



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

* Re: Generator Examples
  2016-09-25  0:32   ` raman
@ 2016-09-25  1:06     ` Michael Heerdegen
  0 siblings, 0 replies; 7+ messages in thread
From: Michael Heerdegen @ 2016-09-25  1:06 UTC (permalink / raw)
  To: raman; +Cc: emacs-devel

raman <raman@google.com> writes:

> P.S. In my quick tests, the copy-sequence was necessary -- If it
> weren't generators would get inefficient quickly because you'd have to
> do a deep copy of the variable you were snapshotting in the closure.

I don't understand what you mean, but it's already late (an example,
maybe?)


Regards,

Michael.



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

* Re: Generator Examples
  2016-09-25  1:03     ` Michael Heerdegen
@ 2016-09-25  2:26       ` T.V Raman
  0 siblings, 0 replies; 7+ messages in thread
From: T.V Raman @ 2016-09-25  2:26 UTC (permalink / raw)
  To: michael_heerdegen; +Cc: emacs-devel, raman

The bit you wrote in response to my "dont understand why nested
iter-yield" might do well in the manual. Also, it might do what it
claims, but why it does it is opaque -- and examples that do something
useful are usually easier to learn from.

Michael Heerdegen writes:
 > raman <raman@google.com> writes:
 > 
 > > The cl-psetq is the right idiom here in that sense -- if you use plain
 > > setq, you'd have to use a temporary variable?
 > 
 > Sure, but I want to avoid to confront people with functions they
 > potentially don't know in these examples, so that they can concentrate
 > on the gist.  A minor issue, sure.
 > 
 > > Thanks for the coroutine example, again that would do well in the
 > > manual -- there is a reference to coroutines either in the manual or
 > > in code comments in generators.el that is presently "intriguing".
 > 
 > AFAIK the original author wanted to add some code for working with
 > coroutines.
 > 
 > > Finally, I still dont understand the nested call to iter-yield in the
 > > manual and if it is correct, it would be good to have it better
 > > explained there.
 > 
 > I think it is correct (behaves exactly as described, and the behavior is
 > what I expect).  What in particular don't you understand?
 > 
 > I think the thing that this part wants to demonstrate is that
 > `iter-yield' behaves normally wrt when it is evaluated (function
 > arguments before function calls etc.; so the inner `iter-yield' is
 > called first, and the outer not before the iterator has been restarted).
 > And the outer `iter-yield' yields a value that is computed from the
 > "return value" of the inner, which is the second argument of the
 > according `iter-next' call.
 > 
 > 
 > Michael.

-- 

-- 



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

end of thread, other threads:[~2016-09-25  2:26 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-22  0:47 Generator Examples raman
2016-09-25  0:16 ` Michael Heerdegen
2016-09-25  0:29   ` raman
2016-09-25  1:03     ` Michael Heerdegen
2016-09-25  2:26       ` T.V Raman
2016-09-25  0:32   ` raman
2016-09-25  1:06     ` Michael Heerdegen

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

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).