First, thanks to all who replied concerning the appropriateness of
newbie questions on this newsgroup.
In Leo Wong's tutorial "Simple Forth" it states:
"Forth programmers generally don't use PICK and ROLL much, preferring
specific to general solutions, and avoiding the temptation to treat
the stack as array"
Other than suspecting that PICK and ROLL are not supported by every
"flavor" of Forth, I don't understand where the issue is. Could
someone please flesh out the above quotation for me, so that I can see
the issue? I *do not* question that there is an issue -- I simply
can't see it for looking. TIA...
The main issue is that, if you would need PICK or ROLL, your stack
handling is too complex and you should refactor the word to avoid
needing such things.
Another issue is that ROLL is usually slow and PICK is often not very
fast, either.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2007: http://www.complang.tuwien.ac.at/anton/euroforth2007/
The issue with ROLL is clearly that it is slow.
PICK does not need to be slow, but it usually shows that you are
juggling with much too much values on the stack.
I think that ROLL is always avoidable, while a small number of
(usually debugging) words may need PICK. I try not to use ROLL or
PICK.
-Helmar
I see the issue now! However could you address Leo's comment that the
stack should not be used as an array. How does this figure into the
use of PICK and ROLL?
OK!! The lesson to learn then, is "keep the stack lean"! Then you
won't "need" to reach so deep for a value. Correct?
Like I replied to Anton, I don't see the correlation of the above with
using the stack as an array, unless Leo means that some folks have
historically used a deep stack to squirrel away values, and *then*
used xPICK to reach in and grab what they need, when they need it. Am
I on the right track?
> I see the issue now! However could you address Leo's comment that
> the stack should not be used as an array. How does this figure into
> the use of PICK and ROLL?
A stack is, by definition, a data structure that is only accessed at
one end. An array, in contrast, may be accessed at any point: you can
ask for the third element of an array just as easily as asking for the
first. PICK and ROLL are therefore array operations, not stack
operations, because they can access any element of the stack.
Andrew.
Can a reader follow the data flow through your code? Use of PICK and
ROLL are symptoms of tangled, difficult to comprehend data flow. We in
this group differ in our tolerance of "stack gymnastics" but I think
nearly everybody agrees that PICK and ROLL are beyond the bounds of sane
coding practice.
> I *do not* question that there is an issue -- I simply
> can't see it for looking. TIA...
>
--
John Doty, Noqsi Aerospace, Ltd.
--
Specialization is for robots.
Got it!
I'm jumping ahead of myself here because I haven't reached the section
of the tutorial that deals with the creation of arrays. Nevertheless,
can PICK and ROLL be used to manipulated off-stack arrays, or would
you use "pointers" to these cells?
Slow is relative. Eg. when the stack is in the CPU cache, PICK/ROLL can
be really fast.
BTW I had an application where the stack base pointer was switched
between different memory targets to shuffle memory cells. This is not
standard but it spared me to define new words when the dictionary was
crammed full.
--
Andreas
-------
MinForth: http://minforth.net.ms/
Well, arguments based on linguistic hair splitting really don't tell you
much about good programming practice. Might just be better to redefine
"stack". Not in this case, though.
>
> I'm jumping ahead of myself here because I haven't reached the section
> of the tutorial that deals with the creation of arrays. Nevertheless,
> can PICK and ROLL be used to manipulated off-stack arrays, or would
> you use "pointers" to these cells?
>
Use pointer arithmetic. Same as integer arithmetic in nearly all Forths.
Of course ;) From a technical point of view I like the implementation
see roll
: roll
0806FDA0 91 xchg eax,ecx
0806FDA1 8B 04 8E mov eax,[ecx*4+esi]
0806FDA4 E3 09 jecxz 0806FDAF
0806FDA6 8B 54 8E FC mov edx,[ecx*4+esi-04]
0806FDAA 89 14 8E mov [ecx*4+esi],edx
0806FDAD E2 F7 loop 0806FDA6
0806FDAF 83 C6 04 add esi,+04
0806FDB2 C3 ret
0806FDB3 ...
I've developed this together with Ron (from Reva) while complaining
about that this ROLL should not be used ;) ESI is the data-stack
pointer and EAX contains TOS.
> BTW I had an application where the stack base pointer was switched
> between different memory targets to shuffle memory cells. This is not
> standard but it spared me to define new words when the dictionary was
> crammed full.
Would you explain it better? I can not imagine much at the moment.
-Helmar
Pointers, but they are usually implicit. When I write new programs, I
define two sets of arrays that have identical syntax and error-free
performance. One is the usual kind, wherein an offset calculated from
the base of the array is used willy-nilly, the other does bounds
checking at run time and complains about out-of bounds calls. I use the
latter until I have convinced myself that no erroneous calls lurk in the
code. Then I use the smaller faster code.
Some array definitions look more intuitive to the user than others. They
take a bit more thought to write.
Jerry
--
Engineering is the art of making what you want from things you can get.
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Ha ;-) our messages crossed (see my other posting)
I once used this technique to shuffle data blocks and individual bytes
in a process signal archive. Another application was for signal
correction in transmitted data blocks with simple Hamming encoding.
...
> Can a reader follow the data flow through your code? Use of PICK and
> ROLL are symptoms of tangled, difficult to comprehend data flow. We in
> this group differ in our tolerance of "stack gymnastics" but I think
> nearly everybody agrees that PICK and ROLL are beyond the bounds of sane
> coding practice.
John,
There's always more than one way to do something, but I hope you'll
agree that PICK is not "beyond the bounds of sane coding practice" for a
routine like .S, which deals directly with how much is on the stack and
where on the stack it is.
Yes, he probably meant something like that. And it's not just
historical. Looking at my students (and sometimes also in c.l.f), a
number of newbies get the idea to use the stack like this; it's
unidiomatic in Forth, though.
Postscript has other language features, and there it is idiomatic to
build arrays on the stack (but apart from building, one still does not
"use the stack as an array" even in Postscript).
Thanks Jerry, John Doty and others.....
For the array / pointers replies!. At this point I need to continue
reading the tutorials and doing the "finger exercises" ;) I've made a
hard-copy of your reply Jerry, for a future exercise. Thanks again!
--
duke
> John Doty wrote:
>
> ...
>
>> Can a reader follow the data flow through your code? Use of PICK and
>> ROLL are symptoms of tangled, difficult to comprehend data flow. We in
>> this group differ in our tolerance of "stack gymnastics" but I think
>> nearly everybody agrees that PICK and ROLL are beyond the bounds of sane
>> coding practice.
>
> John,
>
> There's always more than one way to do something, but I hope you'll
> agree that PICK is not "beyond the bounds of sane coding practice" for a
> routine like .S, which deals directly with how much is on the stack and
> where on the stack it is.
>
It is possible to write .S without PICK. I use a dirty trick: I pop the top
items on the stack in a loop to an otherwise inaccessible part of memory
and push them back in another loop (with the same count) while printing.
This way I don't have to define PICK (nor ROLL).
This implementation of a recursive PICK is even slower than ROLL:
: PICK
dup if swap >r 1- recurse r> swap exit then
drop dup
;
: ROLL
dup if swap >r 1- recurse r> swap exit then
drop
;
--
Coos
CHForth, 16 bit DOS applications
http://home.hccnet.nl/j.j.haak/forth.html
Got it!! I swear on my mother's grave!! I will *never* *never* do
that. ;))
Thanks!
--
duke
I used another way before I learned about PICK. Using DEPTH, I did a
series of DUP . >R then cleaned up with as many R> As I wrote, there's
usually another way. My question to Doty was about its being insane to
use PICK here.
Remarkably well put. If you want an array, setup an array.
> Other than suspecting that PICK and ROLL are not supported by every
> "flavor" of Forth, I don't understand where the issue is.
Forth is a based on the model of a two stack machin and
doesn't require support of arrays inside of the stack. If you
want arrays in memory setup arrays in memory, but don't assume
that the stack is in addressable memory. Neither the standard
nor modern Forth hardware assume that that stacks are in the
regular memory space.
Indexing into stacks with PICK and ROLL isn't really treating them
as stacks. It can make for totally incomprehensibe code when you
see 976543 pick. Are you really expected to have a picture in your
head of a million items at once so that that a reference to the
976543th one means something to you? The idea of a stack is
that you only deal with one end, one or two items at a time.
> Could
> someone please flesh out the above quotation for me, so that I can see
> the issue? I *do not* question that there is an issue -- I simply
> can't see it for looking. TIA...
The inventor of Forth has done things he says attempt to enforce
good style on the programmer. One of them is not having a stack
pointer.
Real hardware stacks don't need stack pointers and can be faster,
smaller,
and lower power than hardware implementations that use pointers
because
pointers requires addressing which is relatively slow compared to an
optimized stack. And not having stack pointers helps the programmer
get away from the confused idea that a stack is always really an array
in
addressable memory.
Without a stack pointer PICK and ROLL become true nightmares of bad
code design. And this helps enforce 'stack use' instead of what
people might do if they are thinking of building 'stack frames' that
are really arrays in memory using pointers.
Most machines have hardware that has pointers and opcodes for
stack access. On implemenations on those machines the stack pointers
are usualy exposed in some way. But even the ANS Standard does not
specify stack pointers, and without them PICK and ROLL would be
even worse than with.
I agree. In LSE64 there is a pick primitive. It is used in just one
place (from boot.lse):
[stack] : count pick , sp
This is a factor for building stack dumps. But a newbie shouldn't be
worried about this extreme special case. No existing LSE64 application
code uses pick.
>
> Jerry
I have always wondered about the famous quote "the stack should not be
used as an array." Up to now I was thinking that the author wanted to
say that the stack should not be abused as an array. But of course, what
he meant was that an array should not be abused as a stack. It is obviously
very bad and inefficient to cast array operations as stack ops.
create foo 1 , 2 , 3 , 4 , 5 , 6 ,
: afoo+1 foo 6 0 DO 1 over +! cell+ LOOP DROP ;
: sfoo+1 foo 6 0 DO @+ swap LOOP >R
6 0 DO 1+ 5 roll LOOP
R> 6 0 DO cell- tuck ! LOOP DROP ;
: .a foo 6 0 DO @+ . LOOP DROP ;
cr .a afoo+1 cr .a
cr
cr .a sfoo+1 cr .a
:-)
-marcel
> The issue with ROLL is clearly that it is slow.
> PICK does not need to be slow, but it usually shows that you are
> juggling with much too much values on the stack.
Of course, it depends, doesn't it?
There is no "roll" in Reva, but we do have "pick" as well as its
opposite, "put". Both can be very useful when used in moderation.
Both are very fast as implemented in Reva.
I'm sure others will chime in with the usual reasons why PICK and ROLL
are frowned on, but the general theme is that if you need either, you
probably haven't carefully thought about the order of items on the stack
and/or you likely have definitions that could be factored better. In
other words, the use of PICK or ROLL is a common "code smell" that
suggests problems elsewhere.
I only wanted to add one of the few times when I've found PICK and ROLL
were actually useful. It's not when writing Forth code, but when
implementing a Forth.
I have a C-based Forth-like language that is very strongly optimized for
size over speed. That is, I don't care at all how fast it is, but I
care very much that it be as small as possible (and still be expressed
in C). In that narrow context, PICK and ROLL can be considered factors
of some of the other major stack operations:
: dup 0 pick ;
: over 1 pick ;
: swap 1 roll ;
: rot 2 roll ;
: nip swap drop ;
All five of these definitions suffer from being slow. But if size is
your primary concern, then the generality of PICK and ROLL can serve as
useful factors to save space.
.S is a supplied by the implementor.
Unless the stack is in hardware, it is unlikely that PICK would be
more appropriate than viewing the stack temporarily as an array where
the two bounds are known.
E.g. ciforth uses DSP@ (memory pointer to top of stack) and S0
(user variable containing stack bottom) with impunity for implementing
.S .
(Scratch head, look at gForth)
Apparently not. gForth uses PICK and DEPTH in .S .
Anyway I prefer PICK as a loadable extension. OTOH .S is indispensable
in the kernel. Glad I can have my own Forth.
>Jerry
Groetjes Albert
--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- like all pyramid schemes -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
The larger point is valid, but as is often the case with you, the
example used to illustrate is ridiculous. And the problem with a
ridiculous example is that people may dismiss the point.
A better example would have been a smaller integer, like 9 pick. It
isn't conceivable that even the wildest abuse of Forth would have 976543
items on the stack. But it is conceivable that a newbie to Forth
might easily construct words that have more than a handful of items on
the stack.
> Without a stack pointer PICK and ROLL become true nightmares of bad
> code design. And this helps enforce 'stack use' instead of what
> people might do if they are thinking of building 'stack frames' that
> are really arrays in memory using pointers.
Again, you start off with a larger valid point and then diminish it with
a statement that doesn't make sense. I seriously doubt people who
write (bad) words that juggle many items on the stack do so because they
have a mental model of building stack frames. What they are doing is
far more simply explained as not putting the effort into factoring their
code and writing monolithic words that try to do everything.
It should also come as no surprise that the same lesson is well-known to
most other programming languages. Writing functions with a large number
of arguments is frowned on in many languages because it suggests you're
writing monolithic functions. It's a "code smell" that is nearly universal.
For _application_ code, I would say that PICK and ROLL almost
universally indicate something is wrong, or at the very least that the
programmer needs to provide a good reason for using them. For other
kinds of code (such as debugging support and cross-language
interfacing), a PICK or ROLL might be just what the doctor ordered.
So no, I don't agree that merely using PICK and ROLL is "beyond the
bounds of sane coding practice." There are narrow contexts where they
make sense.
What would be interesting for someone with access to a large corpus of
Forth source code to do is to search for uses of PICK and ROLL and
summarize their use. Some (probably most) are likely to be
questionable, but some are likely to be useful and knowing the contexts
where it was useful may itself be useful.
This is from (mostly) iForth directories.
Total searched: 20,822 files ( *.f* )
PICK: Found 555 occurrence(s)
2 PICK: Found 241 occurrence(s)
3 PICK: Found 154 occurrence(s)
Note however that 2 PICK and 3 PICK are very frequent (SECOND and
THIRD some Forths call them). A very high proportion of the rest
results from FP code doing array index manipulations like these:
include\gaussj.frt(61): DOUBLE z#{{ 3 PICK DIMS }}create
include\gaussj.frt(65): DOUBLE z#{{ 3 PICK DIMS }}create
include\gaussj.frt(69): DOUBLE z#{{ 3 PICK DIMS }}create
I have to admit that I personally have no objection to n PICK at
all (when n < 5).
Searching for: ROLL
ROLL: Found 47 occurrence(s)
Most of these occur after user or program input sections. Nowadays I'd
solve that with locals. Here's a sample.
examples\ansi\blocks.frt(51): 6 CHOOSE ROLL
examples\ansi\hanoi.frt(152): 2 ROLL 2OVER 2OVER MULTIMOV
examples\asm\go.frt(6): : ALL, CREATE DEPTH 0 ?DO DEPTH 1- ROLL C, LOOP ;
examples\benchmar\towers.frt(159): 2 ROLL 2OVER 2OVER MULTIMOV
examples\c2forth\CinF6\~mak\CinF\enode.F(142): IF \ print("3 ROLL ");
examples\c2forth\CinF6\~mak\FLT\FROLL.F(2): : 3/FROLL SWAP >R DUP >R ROLL R@ ROLL R> ROLL R> ;
examples\games\tictacto.frt(106): DO I ROLL DISP-CELL 'Å‚' EMIT
examples\games\tictacto.frt(113): 1 Rows 1- DO I ROLL 3 .R SPACE 'Å‚' EMIT
examples\graphics\dragon.frt(79): S I - ROLL
examples\graphics\sortdemo.frt(1415): WHILE mdepth 2* 1- >R R@ ROLL R@ ROLL R@ ROLL R> ROLL
examples\graphics\sortdemo.frt(1416): 2 PICK 2 PICK > IF mdepth 2* 1- >R R@ ROLL R> ROLL
examples\misc\blocks.frt(43): 0 1 2 3 4 5 5 FOR 6 CHOOSE ROLL NEXT \ shuffle
examples\misc\mystery.frt(81): : ROLL ( {n}*ix ix -- {n-1}*ix n )
examples\misc\stack.frt(31): 4 ROLL >R SWAP 2SWAP SWAP R>
examples\misc\stack.frt(74): : 4ROLL 4 ROLL ;
examples\misc\stack.frt(78): : INVERT5 4 ROLL >R SWAP 2SWAP SWAP R> ;
examples\misc\stack2.frt(24): 4 ROLL >R SWAP 2SWAP SWAP R>
examples\neural\backprop.frt(362): -- In case you forgot: 0 ROLL does nothing, 1 ROLL is SWAP, 2 ROLL is ROT etc.
examples\neural\backprop.frt(368): DO I ROLL BIT,
examples\neural\backprop.frt(373): DO I ROLL BIT,
examples\nspice\nspiceac.frt(1448): 6 ROLL , 0 , 0 , 0 , 0 , 0 , \ user#
examples\nspice\nspiceac.frt(1451): 5 ROLL , 4 ROLL , \ 'foreground 'background
examples\nspice\nspiceac.frt(1522): 7 ROLL , 6 ROLL , 0 , 0 , 0 , 0 ,
examples\nspice\nspiceac.frt(1525): 5 ROLL , 4 ROLL , \ 'foreground 'background
examples\nspice\nspiceac.frt(1564): 7 ROLL , 6 ROLL , 0 , 0 , 0 , 0 ,
examples\nspice\nspiceac.frt(1567): 5 ROLL , 4 ROLL , \ foreground background
examples\nspice\nspicetr.frt(2738): : >SHOW5x2 9 ROLL 9 ROLL 9 ROLL 9 ROLL 9 ROLL \ <n1> .. <n10> --- <>
examples\nspice\nspicetr.frt(2775): : >SHOW3x2 5 ROLL 5 ROLL 5 ROLL \ <n1> <n2> <n3> <n4> <n5> <n6> --- <>
examples\orkest\manx.frt(1170): \ cnt 1- 0>= IF cnt 1- ROLL SWAP \ **
examples\orkest\linux\manx.frt(1161): \ cnt 1- 0>= IF cnt 1- ROLL SWAP \ **
examples\orkest\win\manx.frt(1163): \ cnt 1- 0>= IF cnt 1- ROLL SWAP \ **
examples\postscri\postscri.frt(147): \G Rotate n elements j times. And you thought the Forth ROLL was shit!
examples\postscri\postscri.frt(154): 0 ?DO S ROLL S ROLL
examples\postscri\postscri.frt(157): SWAP 1- ?NEG >S ?NEG 0 ?DO S ROLL S ROLL LOOP -S ;
examples\UM\balance.frt(329): bitmask #BITS 1- ROLL ( rotate )
include\ix86asm.frt(790): spt 1+ ROLL CL? ( cl or #)
include\ix86asm.frt(792): ( #) spt 1+ ROLL DUP 1 = \ imm8 data ?
include\ix86asm.frt(868): : NROLL DUP >S 0 ?DO S ROLL LOOP -S ; PRIVATE
include\ix86asm.frt(880): : 13MIMEM spt ROLL DUP REG? IF op WMEM, EXIT ENDIF
include\ix86asm.frt(979): spt 2+ ROLL CL?
include\ix86asm.frt(980): IF 1+ TC, ELSE ( #) spt 2+ ROLL TO opnd TC, ENDIF
include\ix86asm.frt(981): DUP REG? IF SWAP RR, ELSE spt ROLL MEM, ENDIF
include\ix86asm.frt(1028): spt ROLL DUP #?
include\ix86asm.frt(1029): IF DROP 272 TC, spt ROLL TO opnd
include\ix86asm.frt(1080): : TESTMEM spt ROLL DUP REG?
include\ix86asm.frt(1143): spt ROLL DUP SEG? ( a segment register?)
include\ix86asm.frt(1184): ELSE spt ROLL MEM,
Found 47 occurrence(s)
-marcel
>What would be interesting for someone with access to a large corpus of
>Forth source code to do is to search for uses of PICK and ROLL and
>summarize their use. Some (probably most) are likely to be
>questionable, but some are likely to be useful and knowing the contexts
>where it was useful may itself be useful.
In the VFX Forth source tree, for code built into VFX Forth
itself, i.e. excluding third party tools such as the Hayes
test suite:
ROLL: 3 occurrences
PICK: 108 occurrences, all but two being 2 PICK or 3 PICK
I would not miss ROLL.
Stephen
--
Stephen Pelc, steph...@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads
> On Mon, 12 Mar 2007 03:40:27 -0400, John Passaniti
> <put-my-firs...@JapanIsShinto.com> wrote:
>
>>What would be interesting for someone with access to a large corpus of
>>Forth source code to do is to search for uses of PICK and ROLL and
>>summarize their use. Some (probably most) are likely to be
>>questionable, but some are likely to be useful and knowing the contexts
>>where it was useful may itself be useful.
>
> In the VFX Forth source tree, for code built into VFX Forth
> itself, i.e. excluding third party tools such as the Hayes
> test suite:
> ROLL: 3 occurrences
> PICK: 108 occurrences, all but two being 2 PICK or 3 PICK
>
> I would not miss ROLL.
Me neither - and for the frequent 2 PICK, I've added an OVER2 word.
--
Bernd Paysan
"If you want it done right, you have to do it yourself"
http://www.jwdt.com/~paysan/
In 219 files of SwiftForth source, I find 85 instances of 2 PICK, no
other PICKs, and no ROLLs. SwiftForth also has THIRD (equivalent to 2
PICK), but the optimizing compiler generates identical code for both.
They're virtually all involved in accessing Windows parameters. I
haven't found any PICKs in SwiftX.
Cheers,
Elizabeth
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310-491-3356
5155 W. Rosecrans Ave. #1018 Fax: +1 310-978-9454
Hawthorne, CA 90250
http://www.forth.com
"Forth-based products and Services for real-time
applications since 1973."
==================================================
Regarding ciforth:
I find 589 files with extension .frt under my PROJECT directory.
(Mostly application codes, and some duplicates.)
None of them uses PICK or ROLL apparently because those words are not
in my kernel and not in my lab file, so they wouldn't compile. (but I
intend to add it to my Library Accessible by Blocks file, some time.)
Interestingly, pick and roll occur infrequently in Postscript
code generated by Forth.
>
>Cheers,
>Elizabeth
>
>--
>==================================================
>Elizabeth D. Rather (US & Canada) 800-55-FORTH
>FORTH Inc. +1 310-491-3356
>5155 W. Rosecrans Ave. #1018 Fax: +1 310-978-9454
>Hawthorne, CA 90250
>http://www.forth.com
>
>"Forth-based products and Services for real-time
>applications since 1973."
>==================================================
PICK and ROLL are in the ANS CORE-EXT wordset and thus
virtually all forths of reasonable size support them.
Sadly PICK and ROLL have become Forth's "bogey men". It's
undeserved. Like any tool, it's a case of when and how it's used.
High usage of PICK or ROLL may indicate too many items on
the stack, in which case one should re-think the problem.
OTOH, one may encounter stack arguments/results that are lengthy
or complicated but cannot be changed e.g. a Windows API call.
In such cases PICK and ROLL can be useful in dealing with them.
Contrary to some claims, PICK as an operator is very efficient.
I do believe the correct phrase is "may be" very efficient.
On processors with a hardware stack rather than a stack
emulated by an index into memory, it can be a pain.
Oddly, it seems to me that on a 6502 a datastack like:
DUP:
DEX
LDA SL+1,X
STA SL,X
LDA SH+1,X
STA SH,X
JMP NEXT ; or else RTS if SRT
provides for the most efficient possible PICK for the
6502 ... no shift required ...
PICK ; no checking implemented
; check: underflow if SH,X nonzero
STY TY
TXA
SEC
ADC SL,X
; check: underflow if CS
; check: underflow if A>TOS
TAY
LDA SL,Y
STA SL,X
LDA SH,Y
STA SH,X
LDY TY
JMP NEXT
So while it is nowhere near as efficient as something
like OVER3, it could be much worse
With indexed addressing, it can be very efficent indeed; on an x86
with ebp as a stack pointer, and eax as TOS;
code pick ( xu ... x1 x0 u -- xu ... x1 x0 xu )
mov eax, [ebp] [eax*4]
next;
ROLL on the other hand is not so great.
--
Regards
Alex McDonald
So for the benefit of this Forth newbie, would it be fair to summarize
the responses to this thread as follows:
1. Factor each "word" as much as possible within practical limits
2. use PICK very sparingly, and *only after* #1 has been achieved
3. Refrain from using ROLL as much as possible, *if ever*
--
duke
> 1. Factor each "word" as much as possible within practical limits
Sensible limits, please. :-)
It's a matter of taste, and even Forth experts disagree about these
things.
> 2. use PICK very sparingly, and *only after* #1 has been achieved
> 3. Refrain from using ROLL as much as possible, *if ever*
If you're a newbie, the only sensible advice is to forget that PICK
and ROLL exist at all. Once you've been using Forth for a while, say
a year or two full-time, then it may be worth considering them.
Andrew.
Pretty close. PICK can be used sparingly, with low numbers (2, 3
maybe). Forget ROLL. In doing your factoring, look for components that
(a) can be re-used; or (b) will make testing easier (e.g. insides of loops).
Actually, from the other side, when sketching, was my limit
in PICK-alikes:
OVER2:
DEX
LDA SL+3,X
STA SL,X
LDA SH+3,X
STA SH,X
JMP NEXT
and the similar for OVER3.
OVER3, in this context, being a factor for 2OVER.
So if either of those sneak into code that I wanted
to wrap a portability harness around, that would be
my use for PICK, to give portable definitions to
OVER2 and OVER3.
> Pretty close. PICK can be used sparingly, with low numbers (2, 3 maybe).
> Forget ROLL. In doing your factoring, look for components that (a) can be
> re-used; or (b) will make testing easier (e.g. insides of loops).
Yes; in fact, Reva has no "roll" even while it has "pick" and "put".
Based on advice from another poster, I've just added "over2" and
"over3" since "2 pick" and "3 pick" are indeed the most frequent uses
of "pick", and these special purpose versions are faster and smaller.
I would like names like THIRD and FOURTH better. Partly because no Forth
word I know has a digit at the end ;-)
And these are existing English words. And they are on Neal Baud's Toolbelt.
The second one looks like the language, but that's not a valid reason I
think, more of a confusion.
Heh. Actually, I have one other word in Reva (core) with a digit at
the end, but it doesn't bother me too much :)
> And these are existing English words. And they are on Neal Baud's Toolbelt.
Yes, I know.
> The second one looks like the language, but that's not a valid reason I
> think, more of a confusion.
I guess I don't like them as well since there is no "SECOND" or
"FIRST". But there is just "over", so "over2" at least connotes what
it does (true, so does THIRD).
On the other hand, it would be easy to confuse with "2over". So maybe
I will change it as you suggest - I'm not too sold on the names yet.
1. You were mixing different CLF posters in unanticipated and incompatible
ways.
2. Quickly now, is THIRD the third word on the stack (base 1) or the fourth
(base 0, like pick)?
That's why I like 2 PICK and 3 PICK : I can remember what the rule is.
-marcel
>> On Mar 14, 12:18 pm, Elizabeth D Rather <erather...@forth.com> wrote:
>>
>>> Pretty close. PICK can be used sparingly, with low numbers (2, 3 maybe).
>>> Forget ROLL. In doing your factoring, look for components that (a) can be
>>> re-used; or (b) will make testing easier (e.g. insides of loops).
>>
>> Yes; in fact, Reva has no "roll" even while it has "pick" and "put".
>>
>> Based on advice from another poster, I've just added "over2" and
>> "over3" since "2 pick" and "3 pick" are indeed the most frequent
>> uses of "pick", and these special purpose versions are faster and
>> smaller.
> I would like names like THIRD and FOURTH better. Partly because no Forth
> word I know has a digit at the end ;-)
Naah, give 'em a name and you'll only be tempted to use 'em. :-)
Andrew.
> > I would like names like THIRD and FOURTH better. Partly because no Forth
> > word I know has a digit at the end ;-)
>
> Naah, give 'em a name and you'll only be tempted to use 'em. :-)
That same argument applies to locals. ;)
> So for the benefit of this Forth newbie, would it be fair to summarize
> the responses to this thread as follows:
>
> 1. Factor each "word" as much as possible within practical limits
> 2. use PICK very sparingly, and *only after* #1 has been achieved
> 3. Refrain from using ROLL as much as possible, *if ever*
That's good, yes.
I'd say that PICK and ROLL are both mostly for the times when you
aren't allowed to factor because of external constraints. So you just
have to do the best you can with what you have, and both PICK and ROLL
can help then.
If you have a deep stack item that you're only going to use once, it's
easier to ROLL it and consume it than to PICK it and dispose of the
copy when it bubbles up to the top. So go ahead and use ROLL and then
*after it's working* you can think about whether you want to change
that to PICK and then DROP or NIP it at just the right time later. Of
course, having it on the stack all the time inbetween will change your
stack depths and you may need to do a lot of rewriting and debugging,
but at least you'll have something that works to fall back on.
PICK and ROLL are for times when you need to deal with deep stack
depths in one word. It's better if you can factor that into smaller
words that each use shallow stacks -- like one of them handles the
bottom stack item or two, and after that's done the next one creates
the next item or two, and when you're done then you call the C
function with 11 inputs. When you need PICK and ROLL it means you
didn't factor that. If you didn't do it because you didn't want to,
then you aren't a Forth programmer. If you didn't see a way to do it
and somebody else sees it quick, then you were having a bad day.
Sometimes there's just no good way to do it.
Why not be completely mnemonic and call them 2PICK and 3PICK ?
Jerry
--
Engineering is the art of making what you want from things you can get.
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
For an example of "arrays on the stack" one need look no further
than GET-ORDER SET-ORDER
Apparently it's "ok" for ALSO PREVIOUS et al to juggle array
items put on the stack by GET-ORDER however it's deemed
"bad form" to use PICK / ROLL . Very amusing.
Dealing with "arrays on the stack" one needs primitives which
can be factored from GET-ORDER SET-ORDER :
>S ( a-addr u -- i*x ) \ place u cells from a-addr to the stack
S> ( i*x a-addr u -- ) \ remove u cells from the stack to a-addr
So perhaps arrays on the stack and PICK / ROLL have a place
after all and we don't have to be scared of them :)
The usage rate of PICK and ROLL is low because one rarely
encounters a situation where it's needed. But that doesn't mean
it won't ever happen.
Newcomers to forth can safely ignore PICK and ROLL while
learning - just be aware that they exist and have their place.
>> So for the benefit of this Forth newbie, would it be fair to summarize
>> the responses to this thread as follows:
>>
>> 1. Factor each "word" as much as possible within practical limits
>> 2. use PICK very sparingly, and *only after* #1 has been achieved
>> 3. Refrain from using ROLL as much as possible, *if ever*
> That's good, yes.
> I'd say that PICK and ROLL are both mostly for the times when you
> aren't allowed to factor because of external constraints.
Aren't allowed to? I don't get it.
Andrew.
> Why not be completely mnemonic and call them 2PICK and 3PICK ?
That might actually be a good idea. But it fossilises the PICK name
that is only mnemonic if you aren't trying to forget that PICK ever
existed.
Some people used to call 2 PICK by the name PLUCK though I didn't
notice it catch on much.
And for 3 PICK I've used the mnemonic 2OVER DROP just as 2OVER NIP
works for 2 PICK . It's wasteful but you don't need it very often and
when you think you do there's usually a better way -- except you might
not have the time to find it.
> > I'd say that PICK and ROLL are both mostly for the times when you
> > aren't allowed to factor because of external constraints.
>
> Aren't allowed to? I don't get it.
Say you're using external functions that you don't get to rewrite.
System calls, or a C library or a GUI interface etc. Or Forth code
that somebody else writes and you don't get to tell him to change the
interface. Like the TIME&DATE routine.
One routine gives you ( -- a b c d e f g h ). You need to call another
routine that requires ( f1(a,h) f2(c,g) f3(b,d) f4(d,h) c g f5(e,f)
-- ) where fN is a function of the following values.
Clearly, you have some stack juggling to do and you can't get out of
it by refactoring the ugly routines for external reasons -- they
aren't your code and you don't get to touch them.
For this particular made-up example (where I chose the pattern pretty
much at random except to make it look hard) my immediate thought is to
use locals. Local variables are great for this kind of complexity.
Forth orthodoxy says that if you have enough complexity to make locals
useful, you'd do better to simplify to the point that locals aren't
actually useful after all. But if you have to accept the complexity,
locals are great.
Or put some values into variables if you don't have locals. g and h
are both used in two widely separated places, I don't want to keep
moving them around on the stack while I try to get other stuff done.
VARIABLE H
VARIABLE G
( f1(a,h) f2(c,g) f3(b,d) f4(d,h) c g f5(e,f) -- )
H ! G ! F5 >R G @ >R \ a b c d R: f5 g
OVER >R \ a b c d R: f5 g c
DUP H @ F4 >R \ a b c d R: f5 g c f4
ROT SWAP F3 >R \ a c R: f5 g c f4 f3
G @ F2 >R \ a R: f5 g c
f4 f3 f2
H @ F1 R> R> R> R> R> R> \ f1(a,h) f2(c,g) f3(b,d) f4(d,h) c g
f5(e,f)
Just 2 variables made a big difference.
But if for some reason you don't want locals or variables, then PICK
is a big help.
\ a b c d e f g h
2SWAP F5 >R \ a b c d g h
OVER >R \ a b c d g h
3 PICK >R \ a b c d g h
2 PICK OVER F4 >R \ a b c d g h
ROT 2 ROLL F3 >R \ a c g h
-ROT F2 >R \ a h
F1 R> R> R> R> R> R>
Summing up, it's best to keep it simple, if you can.
Failing that locals are a big help.
Without locals you can still use global variables to get out of a jam.
And every now and then PICK or ROLL can help.
>> > I'd say that PICK and ROLL are both mostly for the times when you
>> > aren't allowed to factor because of external constraints.
>>
>> Aren't allowed to? I don't get it.
> Say you're using external functions that you don't get to rewrite.
> System calls, or a C library or a GUI interface etc. Or Forth code
> that somebody else writes and you don't get to tell him to change the
> interface. Like the TIME&DATE routine.
> One routine gives you ( -- a b c d e f g h ). You need to call another
> routine that requires ( f1(a,h) f2(c,g) f3(b,d) f4(d,h) c g f5(e,f)
> -- ) where fN is a function of the following values.
> Clearly, you have some stack juggling to do and you can't get out of
> it by refactoring the ugly routines for external reasons -- they
> aren't your code and you don't get to touch them.
Just because someone else passes a ton of args on the stack, doesn't
mean you have to: this is where an interface layer really is called
for. If the external routine is too nasty to use in any reasonable
way, then the right thing to do is create a wrapper with sane
interfaces.
So, call the external routine that returns eight things on the stack,
and marshal them into a struct:
STRUCT Stupid
1 CELLS a
1 CELLS b
1 CELLS c
1 CELLS d
1 CELLS e
1 CELLS f
1 CELLS g
1 CELLS h
END-STRUCT
: MARSHAL ( a b c d e f g h - a) Stupid ALLOCATE
DUP >R Stupid OVER + SWAP DO I ! CELL +LOOP R> ;
Then you have all your args in an easy to handle form, with a single
pointer to the whole argument block. There is some small overhead,
but it will probably vanish into the noise, and the code surrounding
it will be much cleaner and probably faster. This approach is much
better than using local variables because it doesn't prevent
factoring: you can pass the address of the argument block to other
words.
All this heroic activity trying to handle a deep stack is misguided:
if you see a very deep stack, they key is not to find a way to manage
it, but to get rid of it as soon as possible.
Andrew.
> All this heroic activity trying to handle a deep stack is misguided:
> if you see a very deep stack, they key is not to find a way to manage
> it, but to get rid of it as soon as possible.
That makes sense. I thought I did very well with only 2 items removed,
but when you put all of them in a structure then all the complexity is
spilled out right then and the chance for stack errors is drasticly
reduced. And there's nothing to keep you from doing this for yourself,
no matter what the code you can't touch does.
So, do you have any use at all for PICK and ROLL ?
Not yet, but during my career I have only programmed in Forth for
about ten years full-time. Never say never. :-)
Andrew.
Except that ALSO PREVIOUS ONLY DEFINITIONS don't work on the stack (at
least, not in my copy of ANS94). So there's no role for ROLL or PICK.
Section A.16.6.2.0715 of the standard contains an implementation of
the search order words, and ROLL and PICK aren't mentioned at all; in
fact, I'd be hard pressed to suggest how to implement them using ROLL
and PICK, unless you replaced all the OVERs with 1 PICK I suppose.
GET-ORDER and SET-ORDER don't juggle items on the stack either, so I'm
confused by your assertions.
--
Regards
Alex McDonald
[snip]
> Just because someone else passes a ton of args on the stack, doesn't
> mean you have to: this is where an interface layer really is called
> for. If the external routine is too nasty to use in any reasonable
> way, then the right thing to do is create a wrapper with sane
> interfaces.
[snip]
>
> All this heroic activity trying to handle a deep stack is misguided:
> if you see a very deep stack, they key is not to find a way to manage
> it, but to get rid of it as soon as possible.
>
> Andrew.
Thank you Andrew! Thank you J Thomas! Thank you Elizabeth! ... et al !
for your excellent responses. I'm just beginning "to get" what Leo
Brodie (and this community) was/is refering to with respect to "Thinking
Forth".
--
duke | A: Yes. |
| >Q: Are you sure? |
| >>A: Because it reverses the logical flow of conversation. |
| >>>Q: Why is top posting frowned upon? |
I thuoght the point was that if you needed to change the search order
in some drastic way, you might be tempted to use ROLL . Say you have 8
items in the search order and you know that you now need the 7th one
to be searched first. Or even worse, you need to remove the 7th from
the top from the search order.
It might usually be easier to just construct the search order you
want. But whenever the system gives you a long sequence of items on
the stack there's the chance you'll want to do something with the deep
ones. Probably not with SAVE-INPUT . Probably not with GET-ORDER but
maybe. Probably not with deeply-nested control structures, though CS-
PICK and CS-ROLL are what you'll use if you do. Since it's rare, it
was easier to define 2 routines tto do everything than CS-DUP CS-SWAP
CS-OVER CS-ROT etc.
Probably you won't need to manipulate the 7th item in the search
order. But if you do, PICK and ROLL make it easy.
> I would like names like THIRD and FOURTH better. Partly because no Forth
> word I know has a digit at the end ;-)
012345678901234567890123456789012345678901234567890123456789
I would stress that I am not suggesting the words as
standard, they are just the working names. The dictionary in
this system is intended to include a very compact alias
Current name in list:
name-length (byte)
name (name-length bytes)
Special token (optional, byte > 128)
address (word < 32K, if no token, or token > 192)
address2 (word <64K for token > 224)
Next name in List:
Alias is a token that scans through the list for the first
following non alias word in the list and uses that data
field, so a token dictionary entry is name+2 long and
there is no codespace used.
So I'm looking at over2 and over3 as internal system names
and a reasonably short name that is clear to me is the
only real naming criteria there ... I am happy to add any
and as many standard names for the operations as are
required.
If proposing them as standard names, I would probably say
OVER-TWO and OVER-THREE, and I have no real preference
for the process-based name compared to THIRD and FOURTH,
location based names with the PICK process implicit.
> Why not be completely mnemonic and call them 2PICK and 3PICK ?
Because I use OVER and "over two" or "over three" is more
mneumonic to me. Since I haven't ever used PICK, I often have
to look up PICK to check whether DUP is 1 PICK or 0 PICK,
so 2PICK and 3PICK is an anti-mneumonic.
Tricky when you are using the operation as a factor for 2OVER
A valid use I could see for PICK would be a recursive
descent where the first, last, and middle numbers in a sequence
are used to obtain the following numbers in a sequence, and the
descent is known to terminate within a reasonable number of
iterations. Then building the sequence on stack and using
PICK to get to the middle number seems a natural solution.
: 2OVER 3 PICK 3 PICK ;
: 3PICK 2OVER DROP ;
Maybe he has an optimizing compiler ;-)
I wouldn't waste the memory in a threaded implementation.
About giving names, I am leaning toward OVER2 and OVER3 now.
But in my sources, PICK (and ROLL) is only written in some less used
programs, like the Towers of Hanoi and graphics.
As PICK and ROLL are in Core Extensions the user (i.e. myself) must first
include their definitions from a file.
I'd suggest that any code that needs to manipulate the search order to
this degree is seriously out of control. "Probably not" just isn't
strong enough; don't do it!
--
Regards
Alex McDonald
Seven? SEVEN? If you are reaching seven down, you are
either working entirely in your own namespace, or you are
assuming that ONLY FORTH requires only one slot and nobody
ever calls the application with anything else on the stack.
And if you are working entirely in your own namespace, why
would you make it so intricate? KIASAP.
Even if you are reaching four down, doing it by stack
juggling is a headache. Much simpler to say, "OK, I am
using four slots on top", use GET-ORDER, discard the
ones you put there (>R 2DROP 2DROP), write the new order
that you want out of a structure, then PUT-ORDER.
I am dubious anything that can't be done with SWAP and
ROT should be done that way at all ... and actually
also dubious about a lot of the things that could be
done with ROT. 2 target vocabularies, one for private
and one for exported names, with
GET-ORDER >R SWAP R> PUT-ORDER DEFINITIONS
to toggle between the two ... hmmm, maybe.
But that is the argument, isn't it? Otherwise why would it make sense
to think of the GET-ORDER wordlist list as an array?
If you're just going to fiddle with the top couple of wordlists and
then do SET-ORDER then why would PICK or ROLL be useful?
Some of the OOF schemes that were proposed use lots of small (unnamed)
wordlists.
-marcel
ALSO PREVIOUS may not require PICK or ROLL but that doesn't
deny the general case.
Forth now has a history of arrays on the stack e.g. GET/SET-ORDER
SAVE/RESTORE-INPUT . Sometimes one will need to manipulate
the elements in a stack array and PICK / ROLL are ideally suited
when the array is more than a few items.
Isn't everything is relative to the task? - how deep is the stack, how
much parameter juggling is involved etc. No one solution can hold
true for every case. One assumes a proficient programmer will
use the best means; be it PICK ROLL structure or something else.
BTW your MARSHAL example makes the case for having words
to move bulk stack items such as >S S> mentioned elsewhere :)
> "Andrew Haley" <andr...@littlepinkcloud.invalid> wrote in message news:12viimr...@news.supernews.com...
>> ...
>>
>> All this heroic activity trying to handle a deep stack is misguided:
>> if you see a very deep stack, they key is not to find a way to manage
>> it, but to get rid of it as soon as possible.
> Isn't everything is relative to the task? - how deep is the stack,
> how much parameter juggling is involved etc. No one solution can
> hold true for every case. One assumes a proficient programmer will
> use the best means; be it PICK ROLL structure or something else.
Of course, but it's important to think about the whole range of
possibilities. I was making the point that even in cases like this
where PICK and ROLL are touted as the best solution, there are often
(usually? always?) better techniques.
> BTW your MARSHAL example makes the case for having words to move
> bulk stack items such as >S S> mentioned elsewhere :)
It was just a portable example. In practice you'd probably use some
sort of architecture-dependent technique -- maybe switch stack
pointers rather than do a slow copy. :-)
Andrew.
> What would be interesting for someone with access to a large corpus of
> Forth source code to do is to search for uses of PICK and ROLL and
> summarize their use.
PowerMops has no ROLL. So it's safe to say it would not be missed
there.
-Doug
But is the alternative always better? How to say for certain?
Can one rely on the testimonies of others - which can only be
general advice in any case?
The claims against PICK / ROLL have been repeated so often
that it has become a "fait accompli". No longer is it merely a
warning to newcomers to avoid processing many items on stack,
it is now presumed that PICK / ROLL are inherently bad and
that there is always a better way.
> > BTW your MARSHAL example makes the case for having words to move
> > bulk stack items such as >S S> mentioned elsewhere :)
>
> It was just a portable example. In practice you'd probably use some
> sort of architecture-dependent technique -- maybe switch stack
> pointers rather than do a slow copy. :-)
I see an increasing use for >S S>. As forths and apps become more
complex it is likely they'll need to temporarily save/restore items
greater than a cell or two. Using the stack seems a natural. We already
see it in GET/SET-ORDER and SAVE/RESTORE-INPUT. Now that
structures are being seriously proposed for Forth200x, there may be
a need to save/restore them.
> "Andrew Haley" <andr...@littlepinkcloud.invalid> wrote in message news:12vnlf8...@news.supernews.com...
>> Ed <nos...@invalid.com> wrote:
>>
>> > "Andrew Haley" <andr...@littlepinkcloud.invalid> wrote in message news:12viimr...@news.supernews.com...
>> I was making the point that even in cases like this where PICK and
>> ROLL are touted as the best solution, there are often (usually?
>> always?) better techniques.
> But is the alternative always better? How to say for certain?
I'm not absolutely certain about anything. I can only come down to my
previous statement, which is that in my experience PICK and ROLL are
never the right answer. As usual, your mileage may vary.
> Can one rely on the testimonies of others - which can only be
> general advice in any case?
One has to. Until one acquires sufficient experience of one's own,
that's the only possibility -- as with all technical skills.
> The claims against PICK / ROLL have been repeated so often that it
> has become a "fait accompli". No longer is it merely a warning to
> newcomers to avoid processing many items on stack, it is now
> presumed that PICK / ROLL are inherently bad and that there is
> always a better way.
It's not a presumption, which would be to suggest that thy were
rejected without having been considered. You're talking about
something that has been around for many years and has been tried by
many people.
As general advice, if you are tempted to use PICK and ROLL to solve a
knotty stack problem, you are better advised to find out what caused
the stack problem in the first place and make it go away. The most
useful thing I can do is suggest ways to do that.
It works this way: if in doubt, avoid PICK and ROLL. But if you're
not in doubt you won't be seeking advice anyway!
>> > BTW your MARSHAL example makes the case for having words to move
>> > bulk stack items such as >S S> mentioned elsewhere :)
>>
>> It was just a portable example. In practice you'd probably use some
>> sort of architecture-dependent technique -- maybe switch stack
>> pointers rather than do a slow copy. :-)
> I see an increasing use for >S S>. As forths and apps become more
> complex it is likely they'll need to temporarily save/restore items
> greater than a cell or two. Using the stack seems a natural.
The only reason I used a bulk stack copy was that some other language
was using the same resource, and I wanted to get that other language's
block of data out of the way. As a general Forth technique, pushing
blocks of data on the stack strikes me as a bad idea. There are lots
of other places to stash things.
Andrew.