Quote
syba...@chez.com wrote:
>On 18 Dec 1998 17:30:16 +0200, ronka...@cc.helsinki.fi (Osmo Ronkanen)
>wrote:
>>In article <75c0nn$n0...@duke.telepac.pt>,
>>Jose Santos <jcsan...@iname.com> wrote:
>>>Hi all,
>>>What I would like to do is a generic algorithm to take care of the data
>>>regardless of the data type.A good example is a sorting algorithm.
>>>Imagine I want to sort strings instead of integers, or sort records by a
>>>specific field...
>>A good way is to use procedure/function parameters:
>>Type CompareFunc=Function(i,j:integer):Boolean;
>> SwapProc=Procedure(i,j:integer);
>>Procedure ShellSort(first,n:integer;InOrder:CompareFunc; Swap:SwapProc);
>....
>>Now you can write sort and compare routines for the type that you use and
>>pass them as parameter, like:
>>var A:TdataArray;
>>...
>> Procedure Swap(i,j:integer); far;
>> Var
>> Temp:TDataType;
>> Begin
>> Temp:=A[i];
>> A[i]:=A[j];
>> A[j]:=Temp;
>> End;
>>Osmo
>I have a problem with this method of programming :
>Your variable A can be local :
>Look this code :
>Procedure Example;
>var A:TdataArray;
> Procedure Swap(i,j:integer); far;
> Begin
> ...
> End;
> Function Compare(i,j:integer):Boolean;far;
> Begin
> ...
> End;
>Begin
>ShellSort(first,n,:Compare, Swap);
>.....
>End;
>Tp7 doesn't compile this.
>If you don't want to have the variable A in global, how do you make ??
The easiest solution would be to pass the array as an untyped
VAR parameter. Of course that would mean you would have to pass
array elements as untyped VAR parameters rather than simply pass
subscripts to the compare and swap procedures. Of course you
could pass three parameters - array and subscripts.
Procedure Swap(VAR i,j); far;
Begin
...
End;
Function Compare(VAR i,j):Boolean; far;
Begin
...
End;
PROCEDURE Sort(VAR List; first,n,:Compare, Swap);
...
End;
Another solution would be to design the sort procedure to
receive sub-procedures as parameters, as done for a collection's
iterator (ForNext, FirstThat, ...). To call a sub-procedure
you have to have the caller pass a "hidden" parameter. It's
like the "self" passed to an object's method, except in this
case it is the parent procedure's stack frame.
We're getting a little technical here, so let's cover a little
background first. Upon entering a procedure, the TP/BP compiler
generates code to construct a standard stack-frame. This is
simply the code that saves the current value of BP, then loads
BP with the current value of the stack. After the current
position of the stack is obtained, SP is adjusted so as to
allocate space for local variables, intermediate string results,
etc. The stack relative to BP is called the stack frame. Since
BP is static throughout the life of the procedure's instance, it
provides a base from which it can access it's parameters and
local variables.
When a sub-procedure is called, the parent procedure's stack
frame is passed as the "last" parameter. If the calling
procedure is the parent, this means the current value of BP is
pushed to the stack. If one sub-procedure calls another
sub-procedure at the same level, then the calling procedure is
the parent of both. Therefore the value of the caller's
"hidden" parameter is pushed to the stack. Just remember that
it is the stack frame of the called sub-procedure's parent that
is pushed to the stack.
Okay, now let's look at building a sort that expects to receive
a procedure parameter that is a FAR sub-procedure of the CALLER!
Like a collection, the caller *MUST* be the parent procedure.
Osmo already indicated that the first step would be to move the
compare and swap functions out of the sort procedure. You
might find it easier to understand if we did this a step at a
time. If we begin with by using a sub-procedure, the outline
for a typical sort might be --
procedure Sort(n: integer);
Function Compare(i,j: Integer): Boolean; FAR;
Begin
...
End;
VAR i,j: Integer;
Begin
...
If compare(i,j) Then
...
...
End;
The above "If compare" would generate something like
push [BP+nn] { value of i }
push [BP+nn] { value of j }
push BP { parent's stack frame }
push CS { fake far call }
call sort { near call }
When compare is a parameter procedure, then the code looks
something like -
push [BP+nn] { value of i }
push [BP+nn] { value of j }
push CS { fake far call }
call sort { near call }
The trick, if you call it such, is to be able to not only pass i
and j, but to push the stack frame of the procedure that called
the sort procedure. The call would then look like a normal call
to a sub-procedure and the compare function would be able to
access its parent's parameters and local variables.
One solution would be to use BASM to pass all parameters and
make the call. But this wouldn't work for anyone using
something earlier than TP 6.0. If we create a more generic
solution using a local "inline" compare function and a "local"
inline swap procedure as stepping stones, our sort becomes ---
Type CompFunc=Function(i,j:integer):Boolean;
SwapProc=Procedure(i,j:integer);
Procedure ShellSort(n:integer;InOrder: CompFunc; Swap:SwapProc);
Function Comp(x,y: Integer): Boolean;
INLINE( { integer parameters are all ready on the stack }
{ need push caller's stack frame and call compare }
INLINE(
$FF/$76/$00 { push BP[0] }
/$FF/$5E/<compare { call FAR PTR Compare (byte offset)}
);
Function Switch(x,y: Integer): Boolean;
INLINE( { integer parameters are all ready on the stack }
{ need push caller's stack frame and call swap }
INLINE(
$FF/$76/$00 { push BP[0] }
/$FF/$5E/<swap { call FAR PTR Swap (byte offset)}
);
BEGIN{sort}
{ refer to Comp and Switch and NOT InOrder or Swap! }
If Comp(i,j) Then
...
END;
If the main sort is a sub-procedure, as is the usual case in a
quicksort, the nested sub-procedure will need to load it's
parent's frame in order to access the original caller's stack
frame and the local parent's compare and swap parameters.
IIRC, in BASM this becomes:
mov BX,BP[0] { get local parent's stack frame }
push SS:[BX+0] { push original caller's stack frame }
call FAR PTR SS:[BX+OFFSET InOrder]
If you have any problems understanding any of this, I suggest
you stick with passing the array as an untyped parameter. But
you're welcome to write and I'll do my best to help you with any
problems you're having.
Happy holiday every one!
...red