Quote:
>( d -> 96*d )
>: 96* 2DUP 2DUP D+ D+ 2DUP D+ 2DUP D+ 2DUP D+ 2DUP D+ 2DUP D+ ;
>( adr of string -> double-word-encoded-representation )
> DO 2* DUP 63 > IF 1+ THEN
> LOOP
> ELSE 0
>: XDECODE ( Encoded-rep -> ) 3 SPACES 1024 /MOD 4 .R SPACE
> 16 /MOD >R 96 U/MOD 96 /MOD 32 + EMIT 32 + EMIT 32 + EMIT
> R> SPACE 32 + EMIT SPACE ;
>( use as XX word )
>: XX BL WORD XENCODE XDECODE ;
What a beautiful example! Thanks!
I noticed how I studied this code. First, I look at 96* . It looks
like it's supposed to multiply a double number by 96. First it does
2DUP 2DUP D+ D+ which clearly multiples by 3. Then it does 2DUP D+ 5
times, to multiply by 2 5 times. Looks good. I quick write
: 96* D2* D2* D2* D2* D2* 2DUP D2* D+ ;
which should do the same thing. Shorter code, it may run faster or
slower depending. D2* is in the double wordset so it's less portable.
Quote:
>( adr of string -> double-word-encoded-representation )
> DO 2* DUP 63 > IF 1+ THEN
> LOOP
> ELSE 0
Most of the verbiage here comes from a complicated process of setting DO
LOOP indices. The contents don't look that bad, the first loop converts
at most 3 characters to a base-96 double number. If there are more than
3 characters, the 2nd loop hashes them into 6 bits. Finally it gets the
string count and puts it in high bits in the double number.
: XENCODE ( ca -- d )
>R
( d 0 n ) 2* DUP 63 > IF 1+ THEN
This isn't much better. Maybe worse. The loop indices are still
complicated. Different, but not much easier to read. It's usually
wasted effort for me to rewrite other people's code to make it "better".
"If it ain't broke, don't fix it." The other guy's code is very well
tested, mine is not, yet. In rewriting I might make changes that give
subtly different performance in a few cases. If some people use my
version and others use his we could get a maintenance nightmare. But
the exercise has one great virtue -- after I rewrite his code and test
that the results are the same in every case I can find out about, I have
a much deeper understanding of his code.
Quote:
>: XDECODE ( Encoded-rep -> ) 3 SPACES 1024 /MOD 4 .R SPACE
> 16 /MOD >R 96 U/MOD 96 /MOD 32 + EMIT 32 + EMIT 32 + EMIT
> R> SPACE 32 + EMIT SPACE ;
: XDECODER ( d -- ca 4 u )
( d ) <# 1024 /MOD >R
( d ) 16 /MOD 32+ HOLD
( d ) 96 UM/MOD SWAP 32 + HOLD
( q ) 96 /MOD SWAP 32 + HOLD
( q ) 32 + HOLD 0 0 #>
( ca 4 ) R> ;
: XDECODE ( d -- ) XDECODER >R TYPE SPACE R> 4 .R ;
I might want to do something else with the decoded name than print it
immediately. But where should it be stored? My first thought was the
output buffer, but that has to be used pretty quick. I had to print it
before printing the count. I could allot a string and use that, which
is not allowed by the standard while a definition is in progress. ( Are
there systems where it absolutely fails, even if you -ALLOT the space
later?) Use the PAD which isn't re-entrant and isn't there on some
systems? Nothing looked just right.
: XDECODER ( d -- 4chars u )
( d ) 1024 /MOD >R
( d ) 16 /MOD 32 + ROT ROT
( char4 d ) 96 UM/MOD 32 +UNDER
( char4 char3 q ) 96 /MOD 32 +UNDER
( char4 char3 char2 q2 ) 32 +
( c4 c3 c2 c1 ) R> ;
: XDECODE ( d -- ) XDECODER 4 .R SPACE EMIT EMIT EMIT EMIT SPACE ;
Leaving it on the data stack doesn't look great either. 5 items. But
you can print them or compare them. I don't know what's best. Maybe
just print them out and worry about something else when you need it.
In math class they said you don't even have an illusion of communication
unless you can say it back in your own words and the other guy agrees
you got it right. If I can say it to the computer in my own words and
the computer says it matches, that's similar.
Maybe I don't need that much understanding. If I can follow the stack
diagram and the docs, I can use it without understanding it in detail.
But as soon as I use it for something the other guy didn't intend, it's
my lookout whether it works; he could have taken shortcuts that don't
work for me.