How to make dynamic array constant? - arrays

There is any way of making x array constant after the data is read from user? There is any way of making variable not modifiable after it's value is read from user (eg. y)?
program hmm;
uses crt;
var
i, y: word;
x: array of word;
begin
readln(y);
y:=y-1;
SetLength(x,y);
for i := 0 to y do begin
read(x[i]);
end;
readkey;
end.
To make y constant I tried something like this, but it won't work - y will be set as 0.
program hmm;
uses crt;
var
i: word;
x: array of word;
const
{$J+}
y:word = 0;
{$J-}
begin
{$J+}
readln(y);
y:=y-1;
{$J-}
y:=0;
SetLength(x,y);
for i := 0 to y do begin
read(x[i]);
end;
readkey;
end.
Thanks for help.

Yes. Don't change either of them in your code after you set the initial value.
Other than that, there's no way. A dynamic array by definition is changeable, and so is a variable - that's why they have dynamic and variable as names.

Related

string to array in pascal

Hi I am passing string from command line line // - 2,3,4,5,6 and as a param in ip1.
when I run this code its give error "Error: Type identifier expected " and " Fatal: Syntax error, ";" expected but "ARRAY" found".
Please let me know what is the problem.....
program main;
uses SysUtils;
var
output : Array of integer;
var
ip1 : Array of integer;
function add(input1:Array of integer) : Array of integer;
begin
add := input1;
end;
type
TIntegerArray = Array of Integer;
function IntArray(var input:string) : TIntegerArray;
var
p: integer;
begin
p := Pos(',', input);
if p = 0 then
p := MaxInt - 1;
result[0] := Copy(input, 1, p - 1);
result[1] := Copy(input, p + 1);
end;
begin
ip1 := IntArray(ParamStr(1));
output := add(ip1);
write('output ',output,'time',0.0 );
end.
You have so many problems in the code you've posted, it's difficult to know where to start...
You need to move the type declaration for TIntegerArray up closer to the start of your program, so it can be used as the return type of both add and IntArray, as well as the argument to add. array of Integer and TIntegerArray are two different types in Pascal, and can't be interchanged.
You don't check to see if you received any parameters before blindly using them. If they don't exist, your code doesn't work at all. You need to check to make sure you've received the parameters, and produce a useful message with instructions if you don't find them.
You never allocate any space for the IntArray return value. You need to use SetLength to declare the proper number of elements in the array before you can assign anything to them when using dynamic arrays. (See #4 below.)
Your IntArray just presumes there are only two items in input, where your sample command line shows more. You need to use a loop. (
Your IntArray tries to use ParamStr(1) as a var parameter. ParamStr(1) is a constant, and can't be passed as a var anything.
You can't pass an array to write or writeln directly. You have to loop through the elements in the array and output each individually.
(Not really a problem, just info) add does nothing to "add" anything, so it is really poorly named. You should pick names that actually describe what it's doing so that your code is easier to read and understand. (I'm not sure what you intended to do with add, but what you have now does nothing useful.
(Another "not really a problem", but info.) You don't handle any exceptions in case the parameters are not able to be converted to integers. An invalid value provided to StrToInt will raise an exception. You should either use Val or StrToIntDef, or at the very least use a try..except block around the conversion to handle invalid parameters.
(Another "not really a problem".) You don't do anything to pause the program at the end so you can see the output of the write statement, which makes it very hard to test or debug your program from the IDE.
Here's a working (tested) version of your code.
program main;
uses
System.SysUtils;
type
TIntegerArray = Array of Integer;
var
ip1, output: TIntegerArray;
function add(input1: TIntegerArray) : TIntegerArray;
begin
Result := input1;
end;
function IntArray(input:string) : TIntegerArray;
var
p: Integer;
i: Integer; // Tracks current index into Result array
begin
i := 0;
p := Pos(',', input);
while P > 0 do
begin
Inc(i); // Increment index
SetLength(Result, i); // Allocate element in array
Result[i] := StrToInt(Copy(input, 1, P - 1)); // Assign value
System.Delete(input, 1, P); // Remove portion we just read
P := Pos(',', input); // See if there's another comma
end;
// Now get the part after last ',' and add to array also
i := Length(Result);
if (i > 0) and (input <> '') then
begin
SetLength(Result, i + 1);
Result[i + 1] := StrToInt(input);
Input := '';
end;
end;
var
Ctr: Integer;
begin
if ParamCount > 0 then
begin
ip1 := IntArray(ParamStr(1));
output := add(ip1);
Write('Output: ');
for Ctr := Low(output) to High(output) do
Write(output[Ctr], ' ');
// Don't know what this is supposed to do, but...
WriteLn('time', 0.0 );
end
else
begin
WriteLn('ParamCount: ', ParamCount);
WriteLn('Syntax: ', ExtractFileName(ParamStr(0)) + ' <arg,arg[,arg...]>');
end;
ReadLn;
end.
You need to use tintegerarray as return type for add() too, just like you already do for intarray.
After that you will find out that Pascal is strong typed, and doesn't allow assigning strings to parameters.
The ip1:=intarray(paramstr(1)); typecast looks extremely dodgy btw. Maybe lookup the help for paramstr and paramcount again.

Inno Setup Acces violation increment array of strings

I hava an array of strings. I increment this array when it is necessary with the function:
function insertMessageAction(list: TMessagesActions; message: String): TMessagesActions;
var
lenght: integer;
begin
if message <> '' then begin
lenght := GetArrayLength(list);
SetArrayLength(list, lenght +1);
if GetArrayLength(list) > lenght then begin
list[lenght] := message
end;
end;
result := list;
end;
If de increment is of 0 to 2, no problems, by when I increment to 3 lenght, the array is corrupted and 'value' of list is: "Acces violation at address 00403498. Read of address 0000006A".
It is impossible to create more long arrays of 2 items (strings)? There is limit of characters?
Thanks.
First of all, do not use array for your task. Memory reallocation that happens when changing the size of your string array is an expensive operation. What's more, you were trying to return a copy of that input array (in a wrong way), which would be unecessarily inefficient too.
I strongly suggest you to use the TStringList which is intended to be used for a collection of strings. By using TStringList, the whole code from your question would become:
StringList.Add(Message);
But to your question. It is possible, but you need to comply with a few things; at least:
Do not attempt to return an input array passed by reference:
Result := list;
If you need to have an array as a return type for some reason, allocate the size of this output array and copy all the elements from the input array:
InputLen := GetArrayLength(List);
SetArrayLength(Result, InputLen + 1);
for I := 0 to InputLen - 1 do
Result[I] := List[I];
Result[InputLen] := Message;
If you'd still like to stay by array, better pass it by variable parameter:
[Code]
type
TMessagesActions = TArrayOfString;
procedure InsertMessageAction(var AList: TMessagesActions;
const AMessage: String);
var
ArrayLen: Integer;
begin
ArrayLen := GetArrayLength(AList);
SetArrayLength(AList, ArrayLen + 1);
AList[ArrayLen] := AMessage;
end;

how would i convert this from a constant to a variable array?

Uses Graph;
Const Triangle : Array [1..3] Of PointType =
((X: 50; Y: 100),
(X: 100; Y: 100),
(X: 150; Y: 50));
Var Gd, Gm : smallint;
Begin
Gd:=Detect;
InitGraph(Gd, Gm, '');
If GraphResult <> grOk Then Halt(1);
SetFillStyle(7,0);
SetColor(14);
FillPoly(SizeOf(Triangle) Div SizeOf(PointType), Triangle);
ReadLn;
CloseGraph;
End.
I want to be able to input a value for the triangle so the user can move it arround, but it is a constant so that isn't possible unless there is a way to convert this to a variable.
For FreePascal:
Just replace "const" by "var". You can initialize global variables, even complex variables like this one, in this manner, the variable will take this value at the beginning of your program, until you change it.
For TurboPascal:
Constant arrays aren't really constants and you can modify them all you want. For instance, you can do:
Triangle[1].X := 500;
at the beginning of the program and it'll work just fine. See this related question for more details.

How to pass array of shortstring to a method

I would like to make a procedure that take array of shortstring as argument
procedure f(const a, b: Array of shortstring);
I would like to call this with arrays of known length and shortstrings of known length e.g.
var
A, B: array[1..2] of string[5];
C, D: array[1..40] of string[12];
begin
f(A,B);
f(C,D);
end;
This result in an compiler error E2008 Incompatible types.
Why is that? Can I write a procedure that can take arrays of shortstring (any length of arrays/strings)?
Why use shortstring?
The shortstings are fields in an existing record. There are alot of these record with thousand of shortstrings. In an effort to migrate data from turbo power B-Tree Filer to SQL databases one step is to convert the record to a dataset, and the back to a record, to confirm all fields are converted correctly both directions. I have been using CompareMem on the records to check this, but it does not provide enough information as to which field a conversion error is in. Thus a small program was created, which from the record definition can generate code to compare the two records. It was for this code generator I needed a function to compare shortstrings. It ended up using CompareMem on the shortstrings.
A ShortString is 0 to 255 characters long. The length of a ShortString can change dynamically, but memory is a statically allocated 256 bytes, the first byte stores the length of the string, and the remaining 255 bytes are available for characters, whilist string[5] declared in this way allocate only as much memory as the type requires (5 byte + 1 byte for length).
you could use type
type
MyString = string[5];
...
procedure f(const a, b: Array of MyString);
...
var
A, B: array[1..2] of MyString;
begin
f(A,B);
end;
In a similar situation I've used the following:
type
TOpenArrayOfOpenString = record
strict private
FSizeOfString: Integer;
FpStart: PChar;
FArrayLength: Integer;
function GetItemPtr(AIndex: Integer): PShortString;
public
constructor Init(var AFirstString: Openstring; AArrayLength: Integer);
function Equals(const AArray: TOpenArrayOfOpenString): Boolean;
property SizeOfString: Integer read FSizeOfString;
property pStart: PChar read FpStart;
property ArrayLength: Integer read FArrayLength;
property ItemPtrs[AIndex: Integer]: PShortString read GetItemPtr; default;
end;
{ TOpenArrayOfOpenString }
constructor TOpenArrayOfOpenString.Init(var AFirstString: Openstring; AArrayLength: Integer);
begin
FSizeOfString := SizeOf(AFirstString);
FpStart := #AFirstString[0]; // incl. length byte!
FArrayLength := AArrayLength;
end;
function TOpenArrayOfOpenString.Equals(const AArray: TOpenArrayOfOpenString): Boolean;
begin
Result := CompareMem(pStart, AArray.pStart, SizeOfString * ArrayLength);
end;
function TOpenArrayOfOpenString.GetItemPtr(AIndex: Integer): PShortString;
begin
Result := PShortString(pStart + AIndex * SizeOfString);
end;
You could use it like this:
procedure f(const a: TOpenArrayOfOpenString);
var
i: Integer;
begin
for i := 0 to Pred(a.ArrayLength) do
Writeln(a[i]^);
end;
procedure Test;
var
A: array[1..2] of string[5];
C: array[1..40] of string[12];
begin
f(TOpenArrayOfOpenString.Init(A[1], Length(A)));
f(TOpenArrayOfOpenString.Init(C[1], Length(C)));
end;
It's not as elegant as a solution built into the language could be and it is a bit hacky as it relies on the fact/hope/... that the strings in the array are laid out contiguously. But it worked for me for some time now.
type
shortStrings =array[1..2] of string[5];
...
a,b : shortString;
..
procedure rock(a,b : shortStrings);
..
You are combining two different kinds of open array.
First, there is the classic Turbo Pascal "string" (also called "openstring" in Delphi IIRC) which is essentially string[255]. As string[255] is a superset of all shortstrings, the open array aspect simply converts all shortstring types to it.
The "array of xx" syntax is the Delphi (4+?) open array. It is an open array of any type, not just strings, and the syntax to call it is f(nonarrayparam,[arrayelement0,arrayelement1]);
Somehow you seem to mix both syntaxes, and even aggrevate it by adding CONST which sollicits pass by reference and excludes conversions.
I think you assume shortstring has an performance advantage. It has, in some cases. Open array is not one of those cases. Not even in TP :-)

Delphi TBytes - how to copy?

Point is opimization here.
Now:
type TSomeClass=class(TObject)
private
DataWrite: TBytes;
...
end;
Function TSomeClass.GetPacket: TBytes;
begin
SetLength(Result, Length(DataWrite));
Move(DataWrite[0],Result[0],Length(DataWrite));
end;
What I want to achieve:
Function TSomeClass.GetPacket: TBytes;
begin
Result := DataWrite;
end;
Because Arrays in Delphi are pointers to first element, the latter only and only writes 4 bytes so it is MUCH faster. Is this correct?
The one thing you need to be aware of is that different from strings, dynamic arrays are not "copy-on-write".
If you assign a string, or a dynamic array, only the pointer to the data on the heap is copied and the reference count is incremented.
But with a string, if you then write into a string (e.g. s[1] := 'a') which has a reference count > 1, the compiler will emit code which makes sure that the string is copied first. This is not the case with dynamic arrays:
var
s, t: string;
a, b: TBytes;
begin
s := 'abc';
t := s;
t[2] := 'X';
WriteLn(s); //still abc
a := TBytes.Create(1, 2, 3);
b := a;
b[1] := 0;
WriteLn(a[1]); // is now 0 not 2!
So in case of your code, if you change the contents of DataWrite after GetPacket was called, the change will be visible in the TBytes that GetPacket returned.
For the code where you actually make a copy of the array, instead of calling SetLength And Move, you can use:
function TSomeClass.GetPacket: TBytes;
begin
Result := Copy(DataWrite, 0, High(Integer));
end;
That will work but note that you are now working on the same byte array in client code that calls GetPacket. This might be a bad idea. Consider some network library that does some additional compression or encryption on the byte array. This creates a lot of possibilites to interact with your class without using the exposed interface - which is bad. Thus IMHO copying is the better option here.
BTW: How big are the arrays we are talking about here?

Resources