Archived

This topic is now archived and is closed to further replies.

Xorcist

High() and Low() Uh-Oh!

Recommended Posts

Xorcist    122
Exactly how would I go about determining the Upper and Lower Bounds of a two dimensional array''s second dimension? I know High and Low work with the first dimension, but that kinda leaves me hanging. If for instance I have a dynamic multi-dimensional array, thus not knowing the bounds, how could I go about initializing all the elements to a default value? I''m truely lost for an answer on this one... someone please help!

Share this post


Link to post
Share on other sites
MarkyD    127
Is this what you mean? I think it might be, but I''m more than spectacular at misunderstanding the question.

  
var
MyArray: array[0..6,3..7] of integer; // array with any bounds

AnyInt: integer;
begin

// Returns the high bounds of the first dimention, ie. 6

High(MyArray);

// Returns the high bounds of the second dimention, ie. 7

High(MyArray[AnyInt]);
end;


Just put that together quickly now and it works for me.

~ There''s no substitute for failure ~

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
works because

MyArray: array[0..6,3..7] of integer;
equals
MyArray: array[0..6] of array[3..7] of integer;
[/CODE]

Share this post


Link to post
Share on other sites
Xorcist    122
Okay both of you guys really confused me but I think I found the answer. High and Low can be called with a specific dimension in mind, such as Low(MyArray[1]) for the first dimension or High(MyArray[10]) for the tenth dimension if there was one. So I can use the following code to clear/initialize a two dimensional array of integers without knowing the explicit bounds:

    
Procedure Clear(var tmpArray: TArray);
Var
X,Y: Integer;
Begin
for X := Low(tmpArray[1]) to High(tmpArray[1]) do begin
for Y := Low(tmpArray[2]) to High(tmpArray[2]) do begin
tmpArray[X,Y] := 0;
end;
end;
End;



P.S. MarkyD, I'm not quite sure you got your code to work because in this line:

        
// Returns the high bounds of the second dimention, ie. 7

High(MyArray[AnyInt]);


AnyInt has yet to be assigned a value, which is what started my confusion, but I finally realized what you were trying to get across.

Edited by - Xorcist on November 8, 2001 5:47:45 PM

Share this post


Link to post
Share on other sites
MarkyD    127
quote:
Original post by Xorcist
MarkyD, I'm not quite sure you got your code to work


Neither am I.

quote:

AnyInt has yet to be assigned a value, which is what started my confusion.


I wasn't too sure about this either. But I just tried this now, and it worked perfectly...every time:

        
procedure TForm1.FormActivate(Sender: TObject);
var
High: array[1..5,4..57,-5..36,7..45] of integer;
AnyInt: integer;
begin
randomize;
AnyInt:=Random(50000);
Label1.Caption:='High(BigArr): '+IntToStr(High(BigArr));
Label2.Caption:='High(BigArr[?]): '+IntToStr(High(BigArr[AnyInt]));
Label3.Caption:='High(BigArr[?][?]): '+IntToStr(High(BigArr[AnyInt][AnyInt]));
Label4.Caption:='High(BigArr[?][?][?]): '+IntToStr(High(BigArr[AnyInt][AnyInt][AnyInt]));
end;


I would have thought that AnyInt would have had to be within the bounds or the array, too. But after that threw out the same result every time, I guess that number doesn't actually have to be anything special. Strange.

~ There's no substitute for failure ~

Edited by - MarkyD on November 9, 2001 11:36:30 AM

Share this post


Link to post
Share on other sites
MarkyD    127
I''ve just tried your code, and I got an Access Violation error. Here is some more code that you might be interested in...

  

Procedure TForm1.FormActivate(Sender: TObject);
var
BigArr: array[1..5,4..57] of integer;
L0,H0,L1,H1,L2,H2: integer;
begin
L0:=Low(BigArr); // Returns: 1

H0:=High(BigArr); // Returns: 5

L1:=Low(BigArr[1]); // Returns: 4

H1:=High(BigArr[1]); // Returns: 57

L2:=Low(BigArr[2]); // Returns: 4

H2:=High(BigArr[2]); // Returns: 57

end;



That''s your answer. The number inside the brackets doesn''t affect anything - it''s the number of them. If you need to know how many dimensions there are in an array, I haven''t got a clue. I''ll have a look around and see.

~ There''s no substitute for failure ~

Share this post


Link to post
Share on other sites
Xorcist    122
Actually that number in the brackets is important. I just ran a battery of tests and generated a quick console app and found the following:

          
PROGRAM Check;

TYPE
TArray = array[1..10, 2..20, 3..30] of char;

VAR
MyArray: TArray;

Procedure CheckDimensions(var tmpArray: TArray);
Var
ResultH,ResultL: Integer;
Begin

{ Display First Dimensional Bounds }
ResultL := Low(tmpArray);
ResultH := High(tmpArray);
Writeln('Low:', ResultL, ' ', 'High:', ResultH);

{ Display Second Dimensional Bounds }
ResultL := Low(tmpArray[10]);
ResultH := High(tmpArray[10]);
Writeln('Low:', ResultL, ' ', 'High:', ResultH);

{ Display Third Dimensional Bounds }
ResultL := Low(tmpArray[1][2]);
ResultH := High(tmpArray[1][2]);
Writeln('Low:', ResultL, ' ', 'High:', ResultH);

End;

BEGIN { Main }

{ Insert program code here }
CheckDimensions(MyArray);

{ Remove the following two lines before production }
Writeln;
Write('Press any key to continue... ');
Readln;

END. { Main }


Calling Low or High with just the array name is the same as checking the lower and upper bounds of the first dimension of an array. Calling Low or High with the array name and a single bracketed number will return the second dimension, however that bracketed number must be within the range of the first dimension. Try putting 0 or 11 into the second dimensional check of the console app above and you'll get a comipler error. Every other bracketed number you append will move the dimensional check up one rung, but the number within must be within the range of the previous dimension (otherwise the compiler will complain with "Constant expression violates subranges bounds"). So indeed it is not the number that matters but rather the number of subscripts appended, however the subscripts can not be any number, they must be within the range for the given dimension. (Did that make sense?)


Example:

Low(MyArray) checks the lower bound on MyArray[]
Low(MyArray[#1]) checks the lower bound on MyArray[][]
Low(MyArray[#2][#3]) checks the lower bound on MyArray[][][]

#1 must be within the bounds of the array's first dimension
#2 must be within the bounds of the array's first dimension
#3 must be within the bounds of the array's second dimension

it kinda makes sense when you consider exactly what Low and High do, which is nothing more than return a limit on a range.

Edited by - Xorcist on November 9, 2001 2:15:30 PM

Share this post


Link to post
Share on other sites
Xorcist    122
Comma delimited notation seems to work as well:

  
{ Display Third Dimensional Bounds }
ResultL := Low(tmpArray[10,20]);
ResultH := High(tmpArray[10,20]);
Writeln(''Low:'', ResultL, '' '', ''High:'', ResultH);

Share this post


Link to post
Share on other sites
Xorcist    122
Of course now we get back to the problem were, since multi-dimensional arrays need to be declared and passed as user defined types, will retain their actual ranges. Unlike using array of type , to pass single dimensional arrays. Which changes the range of the array inside of the procedure it was passed to, to 0 though elements - 1 (reguardless of what the original range was). Thus we must know exactly how the array was dimensioned in order to get the proper return from High and Low , which would force us to do something along the lines of this to generalize it:

    
{ Display Third Dimensional Bounds }
ResultL := Low(tmpArray[Low(tmpArray),Low(tmpArray[Low(tmpArray)])]);
ResultH := High(tmpArray[High(tmpArray),High(tmpArray[High(tmpArray)])]);
Writeln('Low:', ResultL, ' ', 'High:', ResultH);



Which I'm sure we'll all agree is pretty messy... so, back to my original question. Without knowing the explicit bounds, how would I go about writing a generalized unit procedure that takes in a two dimensional array of integers and clears it. That is without knowing how the "user" declared his array. I'm having a lot of trouble figuring this out, because it always seems I need some sort of pre-defined array type. Which is not what I want because I want the user to be able to define and declare a two dimensional array however he/she likes and my functions should be able to work on it reguardlessly. I've tried messing around with passing arrays as pointers but come up short handed each time. Anyone?

{Why is this so difficult, you'd think such as task would be trivial... at least in any language other than Delphi }

Edited by - Xorcist on November 9, 2001 3:03:30 PM

Share this post


Link to post
Share on other sites
MarkyD    127
I had this post ready yesterday, but after my browser started to slow down, I cut, pasted and saved it just before the thing crashed. Ah well, got there in the end.

[oldpost]

quote:
Original post by Xorcist
Actually that number in the brackets is important.



Actually it isn''t. Kind of.
If the number in the brackets is a constant, then indeed it must be inside that dimension''s bounds. However, if it is a variable, then it doesn''t have to be. Well...not with me anyway.
Anyway, here is some more code:

  

procedure Cleararraysorsomething;
const
NUM_OVER_10 = 11;
var
MyArray: array[1..10,2..20,3..30] of integer;
TempInt,
Result: integer;
begin

{Here, 11 is exceeding the dimension''s bounds, so the compiler will generate an error}
Result:=High(MyArray[11]);

{Here, since NUM_OVER_10 equals 11, the compiler will again generate an error}
Result:=High(MyArray[NUM_OVER_10]);

{Here, TempInt is assigned the value of 11. But since the compiler doesn''t know that TempInt is out of
bounds, the code will still run}
TempInt:=11;
Result:=High(MyArray[TempInt]);
end;


I still don''t believe that the number passed in the brackets is important - even though the compiler seems to think so. High and Low , I think, return the range of the dimension above the number of brackets, like you pointed out. But I don''t think that those procedures actually use the numbers in the brackets - it''s just that the compiler doesn''t realise it.

I''ve rambled enough here. Time to do some more testing...

[/oldpost]

There. Posted it in the end. I''ve got an idea around this, though. This is only a recent thought that I have not put in practise, but if you call a procedure with an out word before the array, it would be cleared before the procedure is ran through. Like so...

  
procedure ClearArray(out SomeArray: array of integer);
begin
end;


If you call that procedure, shouldn''t the array get cleared before the procedure''s lines - even though it has none? Just an idea...

~ There''s no substitute for failure ~

Share this post


Link to post
Share on other sites
Xorcist    122
But, in my case this code won''t work:

  
procedure ClearArray(out SomeArray: array of integer);
begin
end;


because using array of integer expects a singly dimensioned array, and won''t accept arrays with two dimensions or more (I''ve tried).

I see what you mean about the number not mattering, if they''re variables. That actually helps out a hell of a lot because now I don''t need that mess of code I wrote before to abstract the numbers, I can just use a default variable to get my results as such:

  
Procedure Clear(var tmpArray: TArray);
Var X,Y: Integer;
Tmp: Integer;
Begin
Tmp = 0; // <- Actually this isn''t even necessary

for X := Low(tmpArray) to High(tmpArray) do begin
for Y := Low(tmpArray[Tmp]) to High(tmpArray[Tmp]) do begin
tmpArray[X,Y] := 0;
end;
end;
End;


But the question I have now is how the heck can I pass an array to a procedure like the one above, without a pre-defined array type (notice TArray would have to have been defined somewhere and the array would have to be of that exact type to be accepted by the procedure). Like I stated before I need to be able to accept and work on any two dimensional array a user might declare. I''m sure there is way to do that, there just has to be!

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
man you wrote lines over lines but you didn''t think !

type TArray = array of integer;
type T2Array = array of TArray;

et voila a flexible 2d - array.

damn !

Share this post


Link to post
Share on other sites
Xorcist    122
Huh? I''m not sure I follow... how will that help me? I''m not worried about having a flexible two dimensional array, I''m worried about writing a procedure that can take in any two dimensional array, no matter how it was defined or declared, and then being able to perform operations on the entire array. The idea is to make a utility procedure that will be used by other individuals programming in Delphi, so I can''t bind my parameter to any pre-defined type, because I have no idea how the programmer plans to define their two dimensional array.

P.S. The High and Low problem was only part of that problem.

Share this post


Link to post
Share on other sites
MarkyD    127
I don't think it's possible to create just one procedure for all types. You could just have a couple of procedures, one for integers, one for strings, etc. I see what you want to do, but whatever I tried I got:

quote:
Origional quote by my smartarse compiler
Incompatible types: ___ and ___


I'm sure it wouldn't hurt to make a couple of procedures. If you only want one, then you'll probably need to start using pointers or something. Yipee!

~ There's no substitute for failure ~

Edited by - MarkyD on November 11, 2001 11:15:19 AM

Share this post


Link to post
Share on other sites
MarkyD    127
Some ideas:

This uses the Windows ZeroMemery function. I thought that it should zero out where the parameter points, but it doesn't work for some reason. Doesn't stop me from posting it, though. Heh, heh.

      

procedure ClearArray(ToClear: Pointer);
begin
ZeroMemory(ToClear,SizeOf(ToClear));
end;

begin
ClearArray(@MyArray);
end.



This one kinda works, but only if the parameter passed is of type TIntegerArray, whether it is an array or not. It is a start of how to use pointers, though.

        

procedure ClearArray(ToClear: Pointer);
type
TIntegerArray = array of array of integer;
PIntegerArray = ^TIntegerArray
var
TempX,
TempY: integer;
Arr: PIntegerArray;
begin
Arr:=PIntegerArray(ToClear);
for TempX:=Low(Arr^) to High(Arr^) do
for TempY:=Low(Arr^[TempX]) to High(Arr^[TempX]) do
Arr^[TempX][TempY]:=0;
end;

begin
ClearArray(@MyArray);
end.



I've had a few problems running them, though, so I would not be surprised if you find a couple of errors in there.

~ There's no substitute for failure ~

Edited by - MarkyD on November 11, 2001 2:51:06 PM

Edited by - MarkyD on November 11, 2001 2:52:09 PM

Share this post


Link to post
Share on other sites
Xorcist    122
Yeah I kept running into the same wall. I tried using Variants and I tried using Pointers, but I just couldn''t seem to pass a two dimensional array without first having a pre-declared array type. I don''t mind much if I need seperate functions for each different element type, but for right now I''m only concerned about a two dimensional array of integers. However that could be declared as:

  
VAR
MyArray: array[1..10,1..5] of integer;


or

  
TYPE
TArray = array[1..10,1..5] of integer;
VAR
MyArray: TArray;


or

  
TYPE
PArray = ^TArray;
TArray = array [0..0] of integer;
VAR
MyArray: PArray;
//with the actual bounds set in main



or even

  
TYPE
TArray = array of integer;
VAR
MyArray: array of TArray;
//with the actual bounds set in main



etc...

I have no way of knowing how the user would have defined their two dimensional array of integers, or what user defined type names they have used. And without forcing the user to use a pre-defined type of my own creation, my procedure should only care that it''s getting a two dimensional array of integers. But how?!?!?!

I just can''t figure it out! If there are any Delphi Guru''s out there please I need asssitance.

Share this post


Link to post
Share on other sites