I was reading through some old code and found this function to
reverse an array.
reverse {
dup xcheck exch
[ exch dup length 1 sub -1 0 { 2 copy get 3 1 roll pop } for pop ]
exch {cvx} if }
And I wondered if it were possible to do it in-place, without creating
a new array. I'm primarily using this for my args-begin function
which does an {exch def} forall to assign names to local variables
and parameters. The reverse is used to pre-process the array
of names so it can be written left-to-right but operated top-down
against the stack.
I wrote it two ways but both are ugly. Is there a nicer way to do it?
Does it require the invention of a fancy new control structure for
a super "for" loop with 2 indices? I've almost done that with the
second try here. It looks almost ready to factor out a new kind
of loop.
{
%dup length 2 idiv -1 0 { % [A B C ..] idx(C)
0 1 2 index 2 idiv { % [A B C ..] idx(A)
1 index length 1 index sub % [A B C ..] idx(A) dst(A)
3 copy 3 copy pop get % [] i(C) d(C) [] i d A
4 copy pop % [] i d [] i d A [] i d
3 copy exch pop get % [] i d [] i d A [] i d .
exch pop put % [] i d [] i d A ([. B C ..])
3 -1 roll pop put % [] i d ([. B C .. A])
pop pop % []
} for
} @pop
{ % [A B C ..]
dup length 0 exch dup 2 div exch 1 sub % [ABC..] lo mid hi
{ % [ABC..] lo mid hi
3 copy gt exch % [] lo mid hi mid>hi lo
3 index gt or % [] lo mid hi mid>hi||lo>mid
{exit} if
4 copy exch pop % [] l m h [] l h
3 copy 6 copy % [] l m h [] l h [] l h [] l h [] l h
exch pop get % [] l m h [] l h [] l h [] l h [h]
4 1 roll pop get % [] l m h [] l h [] l h [h] [l]
5 1 roll exch pop put % [h] l m h [h] l h [l]
3 -1 roll pop put % [hl] l m h
3 2 roll 1 add 3 1 roll 1 sub
} loop
}
Aside, aside: I haven't actually run and tested this. I'm 90% sure that "counttomark 1 add index" correctly grabs the thing just below the mark,
but I reckon there's a 10% chance I'm wrong about that.
3 copy 6 copy % [] l m h [] l h [] l h [] l h [] l h
On Friday, October 22, 2021 at 1:24:52 AM UTC-5, luser droog wrote:
I was reading through some old code and found this function to
reverse an array.
reverse {
dup xcheck exch
[ exch dup length 1 sub -1 0 { 2 copy get 3 1 roll pop } for pop ]
exch {cvx} if }
And I wondered if it were possible to do it in-place, without creating
a new array. I'm primarily using this for my args-begin function
which does an {exch def} forall to assign names to local variables
and parameters. The reverse is used to pre-process the array
of names so it can be written left-to-right but operated top-down
against the stack.
I wrote it two ways but both are ugly. Is there a nicer way to do it?
Does it require the invention of a fancy new control structure for
a super "for" loop with 2 indices? I've almost done that with the
second try here. It looks almost ready to factor out a new kind
of loop.
{Alright. Had to sleep on it. Two loops, sequentially. No new arrays
%dup length 2 idiv -1 0 { % [A B C ..] idx(C)
0 1 2 index 2 idiv { % [A B C ..] idx(A)
1 index length 1 index sub % [A B C ..] idx(A) dst(A)
3 copy 3 copy pop get % [] i(C) d(C) [] i d A
4 copy pop % [] i d [] i d A [] i d
3 copy exch pop get % [] i d [] i d A [] i d .
exch pop put % [] i d [] i d A ([. B C ..])
3 -1 roll pop put % [] i d ([. B C .. A])
pop pop % []
} for
} @pop
{ % [A B C ..]
dup length 0 exch dup 2 div exch 1 sub % [ABC..] lo mid hi
{ % [ABC..] lo mid hi
3 copy gt exch % [] lo mid hi mid>hi lo
3 index gt or % [] lo mid hi mid>hi||lo>mid
{exit} if
4 copy exch pop % [] l m h [] l h
3 copy 6 copy % [] l m h [] l h [] l h [] l h [] l h
exch pop get % [] l m h [] l h [] l h [] l h [h]
4 1 roll pop get % [] l m h [] l h [] l h [h] [l]
5 1 roll exch pop put % [h] l m h [h] l h [l]
3 -1 roll pop put % [hl] l m h
3 2 roll 1 add 3 1 roll 1 sub
} loop
}
created. It uses a fancy loop I already wrote called "fortuple"
array n proc fortuple --
for each invocation of proc, the stack contains successive n-tuples
of elements from array using getinterval.
So, by doing `1 {} fortuple` on the array, I fill the stack with little one- element subarrays of the original array. Then a little stack juggling
to grab a copy of the original array. And then a regular old forall
loop to put each value in its target box, consuming one of the
subarrays on each iteration.
{
[ 1 index 1 {} fortuple counttomark 1 add index { % [...] [ [0] [1] .. [n-1] 0
0 exch put
} forall pop
}
Aside: I considered "dup [ exch" instead of "[ 1 index" but that executes
two operators instead of just one. Probably an insignificant useless microoptimization but that's why I did that.
Aside, aside: I haven't actually run and tested this. I'm 90% sure that "counttomark 1 add index" correctly grabs the thing just below the mark,
but I reckon there's a 10% chance I'm wrong about that.
I was reading through some old code and found this function to
reverse an array.
And I wondered if it were possible to do it in-place, without creating
a new array.
On 22/10/2021 07:24, luser droog wrote:
I was reading through some old code and found this function to/reverse {
reverse an array.
And I wondered if it were possible to do it in-place, without creating
a new array.
aload dup length 1 sub
0 exch 1 exch
{ exch dup 4 2 roll exch put } for
} def
[ 1 2 3 4 5 ]
dup
reverse
pstack
On 22/10/2021 07:24, luser droog wrote:
I was reading through some old code and found this function to/reverse {
reverse an array.
And I wondered if it were possible to do it in-place, without creating
a new array.
aload dup length 1 sub
0 exch 1 exch
{ exch dup 4 2 roll exch put } for
} def
[ 1 2 3 4 5 ]
dup
reverse
pstack
I was reading through some old code and found this function to
reverse an array.
...
And I wondered if it were possible to do it in-place, without creating
a new array.
On 22/10/21 5:24 pm, luser droog wrote:
I was reading through some old code and found this function toMy first idea was this:
reverse an array.
...
And I wondered if it were possible to do it in-place, without creating
a new array.
/reverse {
dup aload
0 1 2 index length 1 sub {
1 index exch 4 -1 roll put
} for
pop
} bind def
It works, is fast, but uses a lot of stack if the array is large.
So I came up with this, which uses minimal stack and gives similar performance (when bound):
/reverse {
dup xcheck exch
dup length 1 sub
0 1 3 index length 2 idiv 1 sub {
2 index exch
1 index 3 index get
2 index 4 index 1 index 4 index get put
put
1 sub
} for
pop
exch {cvx} if
} bind def
By the way, I liked the xcheck. Obviously that's something I borrowed.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 491 |
Nodes: | 16 (2 / 14) |
Uptime: | 112:24:32 |
Calls: | 9,684 |
Files: | 13,725 |
Messages: | 6,175,940 |