Path: csiph.com!eternal-september.org!feeder.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: Carlos Newsgroups: comp.lang.postscript Subject: Re: decoupled for-loop/iterator-variable construct Date: Sat, 17 Sep 2016 15:04:34 +0200 Organization: A noiseless patient Spider Lines: 144 Message-ID: <20160917150434.53428155@samara.DOMA> References: <5a5eafe0-89fc-4a5f-b450-06b8db73c61e@googlegroups.com> <74bb3f1c-0602-46b6-b8cb-d2d78a237e68@googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Injection-Info: mx02.eternal-september.org; posting-host="14ae01f255251c16afab023606f4d0b9"; logging-data="18201"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19km9qQiNSvNZq0vgXN3uKCrux/9AoPX7s=" X-Newsreader: Claws Mail 3.13.2 (GTK+ 2.24.30; x86_64-pc-linux-gnu) Cancel-Lock: sha1:akaMLSfg9xHmgNcPM2zdFIusA3M= Xref: csiph.com comp.lang.postscript:2817 [luser droog , 2016-09-14 22:58] > external iterator That's a great idea. I started to play with it, and I found it makes a lot of things easier. I generalized it to a makeiterator function: % obj1...objn n procnext procnext? -> userprocnext userprocnext? You pass to it: - some objects you need to keep track of the iteration - n, the number of objects - a proc, which gets those objects and returns the next iteration item(s) and a new set of objects with the updated iteration status - another proc, which receives the objects and returns whether there are more items and it returns two procs that don't take any arguments: - one that returns the next iteration item(s) - and one that returns true or false, whether there is a next iteration item For example, a function to create "for" iterators like yours would be constructed this way: % start inc end -> next next? /foriter { 3 % i inc end -> i i+1 inc end { 2 index 2 index add 3 1 roll } % i inc end -> t|f { exch 0 lt { gt } { lt } ifelse } makeiterator } def GS> 1 3 6 foriter GS<2> /next? exch def /next exch def GS>next = 1 GS>next? = true GS>next = 4 GS>next? false I struggle writing functions that take procs and execute them inside loops without stack or dictionary interference. With external iterators that problem doesn't exist anymore! You just have to yield the next value, and let "{ } loop" take care of the looping. With a little syntactic sugar (the function "done") to hide the details they almost look like control structures: GS>(External iterators are great) 9 eachslice { == } done (External ) (iterators) ( are grea) I think this idea can be extended to filters and mappers. I'll get into it when I get tired of writing all kind of iterators :) Here is the code of makeiterator and done, and a couple of iterators: % obj1...objn n nextproc next?proc -> unextproc unext?proc /makeiterator { 4 dict begin /next?proc exch def /nextproc exch def array astore /arr exch def /unext? [ /arr load /aload cvx /pop cvx /next?proc load /exec cvx ] cvx def [ /unext? load /exec cvx /not cvx { exit } /if cvx /arr load /aload cvx /pop cvx /nextproc load /exec cvx /arr load /astore cvx /pop cvx ] cvx /unext? load end } def % next next? proc -> - /done { exch pop % { next proc } loop [ 3 1 roll /exec cvx exch 1 index ] cvx loop } def % start inc end -> next next? /foriter { 3 % i inc end -> i i+1 inc end { 2 index 2 index add 3 1 roll } % i inc end -> t|f { exch 0 lt { gt } { lt } ifelse } makeiterator } def % arr -> n n? /eachwithindex { 0 2 % arr i -> arr[i] i arr i+1 { 2 copy get 1 index 4 2 roll 1 add } % arr i -> t|f { exch length lt } makeiterator } def % arrayn ... array1 n -> nextproc next?proc /eachinparallel { % get minimum length 1 dict begin /n exch def dup length 1 1 n { index length 2 copy gt { exch } if pop } for 0 n % an...a1 len idx n n 3 add end % an...a1 len i n -> an[i]...a1[i] an...a1 len i+1 n { 2 dict begin /n exch def /i exch def n -1 1 { index i get n 2 add 1 roll } for i 1 add n end } % an...a1 len i n -> t|f { 3 1 roll gt exch { exch pop } repeat } makeiterator } def % arr slicesize -> next next? /eachslice { 0 3 % arr n i -> arr[i..i+n] arr n i+1 { 3 copy exch getinterval 4 1 roll 1 index add } % arr n i -> t|f { add exch length le } makeiterator } def --