Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!eternal-september.org!feeder.eternal-september.org!mx02.eternal-september.org!.POSTED!not-for-mail From: Carlos Newsgroups: comp.lang.postscript Subject: Re: map and higher order functions Date: Sun, 16 Nov 2014 01:37:57 +0100 Organization: A noiseless patient Spider Lines: 113 Message-ID: <20141116013757.0dacc25d@samara.DOMA> References: <20141030172728.0983c6bc@samara.DOMA> <95798773-17c6-41d3-8d80-06d1784a7e95@googlegroups.com> <20141102001537.515de3e5@samara.DOMA> <1cc8e78b-6698-4bab-b63c-9475cc63a5c3@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="f4fcdd466dcf73c2e008a0d28a614b04"; logging-data="29313"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18mVUhr7eTJUGyl/V7ElVq9joXDAC54vJI=" X-Newsreader: Claws Mail 3.10.1 (GTK+ 2.24.24; x86_64-pc-linux-gnu) Cancel-Lock: sha1:55mqwRw6Nk5wo/8cYXAepYxMnFs= Xref: csiph.com comp.lang.postscript:2095 [luser- -droog , 2014-11-15 00:43] [...] > There is a possible problem here. Since `bind` applies to all > subarrays, and the user-proc has been embedded directly into this > array. > > instead of > > > //proc exec > > it might be better to do > > //mydict /proc get exec > > so the procedure is not directly embedded. > > Applying `bind` would break code that tries to use operator names as > variables, like > > { > /length 5 def > length dup mul % length^2 > ... > } > > The executable name `length` in the second line would be replaced by > the postscript operator if `bind` were applied. I didn't think of that. The problem is worse, because the string is tokenized at the time /map is executed, and by that moment the user could have already redefined some operators (presumably he wouldn't do that at library loading time). /length 5 def [ 1 2 3 ] { length add } map So, basically, inside /map, the code surrounding " exec" should be bound before any operator is redefined, but the "" should be left as-is. One solution can be to write the code as usual (not in a string), using placeholders for the objects. That way, it would be bound at definition time. When /map is called, the placeholders are replaced with the objects. Basically, doing the "({ //x }) token" thing by hand, just so everything that isn't a //name can be bound at definition time. Here is a new version of map that uses that approach, plus two helper procedures: % map /map { 4 dict begin /,proc exch def /,arr exch def /,res ,arr length ,arr type /stringtype eq { string } { array } ifelse def /,i 1 array def { 0 1 /,arr length 1 sub { % for dup /,i 0 3 -1 roll put /,arr exch get /,proc exec /,res /,i 0 get 3 -1 roll put } for /,res } deepcopy dup currentdict replaceall end exec } bind def % copies array recursively % deepcopy /deepcopy { dup xcheck exch dup length array copy dup length 1 sub 0 exch 1 exch { % for % a i 2 copy 2 copy get dup type /arraytype eq % a i a i e ? { % ifelse deepcopy put } { pop pop pop } ifelse pop } for exch { cvx } if } bind def % recursively replaces elements in found in % replaceall - /replaceall { 1 index length 1 sub 0 1 3 -1 roll { % for 0 1 length-1 3 copy 3 -1 roll exch % a d i d a i get % a d i d e 2 copy known % a d i d e ? % ifelse { % a d i d e get % a d i v 3 index 3 1 roll % a d a i v put } % else { % a d i d e dup type /arraytype eq % a d i d e ? { exch replaceall } { pop pop } ifelse pop } ifelse % a d } for pop pop } bind def Carlos. --