One row, multiple inputs? - pascal

In pascal, as you might know, you can assign multiple variables values in one single line (as long as you have variables to catch them in):
var x, y, z: integer;
readln(x, y, z, etc...);
But what if i wanted to have only one variable, which would sequentially recieve those values which sit, practically, in the void?
Let me explain:
Number of values?
>3 (for example)
Insert values:
4 6 9
Now, these values, would periodically be assigned to 'a' for example, and once i'm done with the number 4, i want it to recieve 6, and 9 afterwards. Is there any way i can do this?

In Extended Pascal, ISO 10206, you can do the following:
program lists(input, output);
procedure processList(length: integer);
var
list: array[1..length] of integer;
var
n: integer;
begin
writeLn('Insert ', length:1, ' values:');
for n := 1 to length do
begin
read(list[n]);
end;
{ Here you can further process `list` }
end;
var
n: integer;
begin
writeLn('Number of values?');
readLn(n);
processList(n);
end.
A better implementation uses schemata for this task:
program lists(input, output);
type
list(length: integer) = array[1..length] of integer;
var
n: integer;
l: ^list;
begin
writeLn('Number of values?');
readLn(n);
{ dynamically allocate memory for `l` }
new(l, n);
writeLn('Insert values:');
for n := 1 to l^.length do
begin
read(l^[n]);
end;
dispose(l);
end.
At compile-time undiscriminated schema data types can only be handled via pointers, which is always a little unpleasant for us programmers, but it works.

Related

TStringList.CustomSort: Compare() with variables

I am trying to custom sort a TStringList by a column in a .CSV file. My code below works (slowly, about 14 seconds for 200,000+ lines):
function Compare(List: TStringList; Index1, Index2: Integer): Integer;
function ColStr(const Ch: Char; const S: String; First, Last: Integer): String;
var
p1, p2: Integer;
function GetPos(const N: Integer; Start: Integer = 1): Integer;
var
I, Len, Count: Integer;
begin
Result := 0;
Len := Length(S);
if (Len = 0) or (Start > Len) or (N < 1) then Exit;
Count := 0;
for I := Start to Len do begin
if S[I] = Ch then begin
Inc(Count);
if Count = N then begin
Result := I;
Exit;
end;
end;
end;
end;
begin
p1 := GetPos(4, 1); // 4 should be a variable
p2 := GetPos(5, 1); // 5 should be a variable
if Last = 0 then Result := Copy(S, p1 + 1, length(S)) else Result := Copy(S, p1 + 1, p2 - p1 - 1);
end;
begin
Result := AnsiCompareStr(ColStr(',', List[Index1], 0, 1), ColStr(',', List[Index2], 0, 1));
end;
What I would want to do is not have this hard-coded but (where commented "should be a variable" depending on which column to sort). I know I can't use:
function Form1.Compare(List: TStringList; Index1, Index2: Integer): Integer;
for inserting variables, as I get the error:
Incompatible types: 'method pointer and regular procedure'.
I have searched through SO looking for instances of this error but cannot find one that fits my question. I would appreciate any pointers in the right direction.
This has to be done with Delphi 7 and Windows 11.
TStringList.CustomSort() does not let you pass in extra parameters, and it does not accept class methods or anonymous procedures. But, what it does do is pass the actual TStringList itself to the callback, so I would suggest deriving a new class from TStringList to add extra fields to it, and then you can access those fields inside the callback, eg:
type
TMyStringList = class(TStringList)
public
Count1: Integer;
Count2: Integer;
end;
function Compare(List: TStringList; Index1, Index2: Integer): Integer;
...
p1 := GetPos(TMyStringList(List).Count1, 1);
p2 := GetPos(TMyStringList(List).Count2, 1);
...
begin
...
end;
...
List := TMyStringList.Create;
// fill List ...
List.Count1 := ...;
List.Count2 := ...;
List.CustomSort(Compare);
So you are performing searching for k-th occurence of Ch and substring creation at every comparison.
You can optimize this process - before sorting make list/array of stringlists, created from every string, separated by needed character - use DelimitedText.
Inside compare function just work with this array and column numbers - sadly, you have to define them as global variables in current unit (for example, after Form1: TForm1)

Dynamic array in Turbo Pascal

I am working on my school project and I would like to use Dynamic (not static) array. I worked with ObjectPascal, so I am used to some syntax. But now I am programming in the old TurboPascal (I am using Turbo Pascal 7 for Windows).
It doesn't seem to know the ObjectPascal, so I thought, that you Turbo Pascal doesn't know dynamic arrays.
Could anyone tell me, if my theory is right or not? I tried to google, but I was not succesfull.
Basicly I am asking "how is it with dynamic arrays in Turbo Pascal 7" ?
Thank you for all reactions.
As MartynA says, there is no dynamic array type in Turbo Pascal. You need to manually allocate memory using pointers, and be careful if you use rangechecks.
Typically you define an array type
TYPE
TArrayT = array[0.. ((65535-spillbytes) div sizeof(T))-1] of T;
where spillbytes is a constant for a small deduction because you can't use the whole 64k, see what the compiler accepts. (Probably this deduction is for heapmanager structures inside the 64k block)
Then you define a pointer
PArrayT= ^TArrayT;
and a variable to it
var
P : PArrayT;
and you allocate nrelement elements using getmem;
getmem(P,SizeOf(T) * nrelements);
and optionally fill them with zero to initialize them:
fillchar(p^,SizeOf(T) * nrelements,#0);
You can access elements using
p^[index]
to free them, use freemem using the exact opposite of the getmem line.
freemem(P,Sizeof(T)*nrelements);
Which means you have to save the allocated number of elements somewhere. This was fixed/solved in Delphi and FPC.
Also keep in mind that you can't find bugs with rangechecking anymore.
If you want arrays larger than 64k, that is possible, but only with constraints, and it matters more which exact TP target (dos, dos-protected or Windows you use) I advise you to search for the online SWAG archive that has many examples. And of course I would recommend to go to FreePascal/Lazarus too where you can simply do:
var x : array of t;
begin
setlength(x,1000000);
and be done with it without additional lines and forget about all of this nonsense.
I'm using Turbo Pascal 5.5 and to create a dynamic array, perhaps the trick is to declare an array with zero dimension as follows:
dArray = array [0..0] of integer;
And then declare a pointer to that array:
pArray = ^dArray ;
And finally, create a pointer variable:
ArrayPtr : pArray;
You can now reference the pointer variable ArrayPtr as follows:
ArrayPtr^[i]; { The index 'i' is of type integer}
See the complete example below:
{
Title: dynarr.pas
A simple Pascal program demonstrating dynamic array.
Compiled and tested with Turbo Pascal 5.5.
}
program dynamic_array;
{Main Program starts here}
type
dArray = array [0..0] of integer;
pArray = ^dArray ;
var
i : integer;
ArrayPtr : pArray;
begin
for i := 0 to 9 do { In this case, array index starts at 0 instead of 1. }
ArrayPtr^[i] := i + 1;
writeln('The Dynamic Array now contains the following:');
writeln;
for i := 0 to 9 do
writeln(ArrayPtr^[i]);
end.
In this example, we have declared the array as:
array[0..0] of integer;
Therefore, the index starts at 0 and if we have n elements, the last element is at index n-1 which is similar to array indexing in C/C++.
Regular Pascal arrays start at 1 but for this case, it starts at 0.
unit Vector;
interface
const MaxVector = 8000;
// 64 k div SizeOf(float); number of float-values that fit in 64 K of stack
VectorError: boolean = False;
// toggle if error occurs. Calling routine can handle or abort
type
VectorStruc = record
Length: word;
Data: array [1..MaxVector] of float;
end;
VectorTyp = ^VectorStruc;
procedure CreateVector(var Vec: VectorTyp; Length: word; Value: float);
{ Generates a vector of length Length and sets all elements to Value }
procedure DestroyVector(var Vec: VectorTyp);
{ release memory occupied by vector }
procedure SetVectorElement(var Vec: VectorTyp; n: word; c: float);
function GetVectorElement(const Vec: VectorTyp; n: word): float;
implementation
var ch: char;
function WriteErrorMessage(Text: string): char;
begin
Write(Text);
Read(WriteErrorMessage);
VectorError := True; // toggle the error marker
end;
procedure CreateVector(var Vec: VectorTyp; Length: word; Value: float);
var
i: word;
begin
try
GetMem(Vec, Length * SizeOf(float) + SizeOf(word) + 6);
except
ch := WriteErrorMessage(' Not enough memory to create vector');
exit;
end;
Vec^.Length := Length;
for i := 1 to Length do
Vec^.Data[i] := Value;
end;
procedure DestroyVector(var Vec: VectorTyp);
var
x: word;
begin
x := Vec^.Length * SizeOf(float) + SizeOf(word) + 6;
FreeMem(Vec, x);
end;
function VectorLength(const Vec: VectorTyp): word;
begin
VectorLength := Vec^.Length;
end;
function GetVectorElement(const Vec: VectorTyp; n: word): float;
var
s1, s2: string;
begin
if (n <= VectorLength(Vec)) then
GetVectorElement := Vec^.Data[n]
else
begin
Str(n: 4, s1);
Str(VectorLength(Vec): 4, s2);
ch := WriteErrorMessage(' Attempt to read non-existent vector element No ' +
s1 + ' of ' + s2);
end;
end;
procedure SetVectorElement(var Vec: VectorTyp; n: word; C: float);
begin
if (n <= VectorLength(Vec)) then
Vec^.Data[n] := C
else
ch := WriteErrorMessage(' Attempt to write to non-existent vector element');
end;
end.
As long as your data fit on the stack, i.e., are smaller than 64 kB, the task is relatively simple. The only thing I don't know is where the 6 bit of extra size go, they are required, however.

Pascal, reading unknown number of integers

My question is how can I read some number of integers that user enters on standard input, and place them in array.However I don't how many numbers user will enter and i can't ask him that? User enters numbers in one line.
Okay i have just one more answer i would like to add.Thanks all for your help this is code written based on suggestions.I added a line for writting array backwards just for you can see that it has readed well.
program backo;
var niz:array [1..100] of integer;
n, i:integer;
begin
i:=1;
writeln('enter elements of array');
read(niz[i]);
while not eoln do
begin
i:=i+1;
read(niz[i]);
end;
for n:=i downto 1 do
writeln(niz[n]);
end.
Ok, based on comments there is three ways demonstrated:
program readmultiint;
{$mode objfpc}{$H+}
uses
StrUtils;
const
CMaxValues = 3;
var
s: string;
darr: array of Integer;
sarr: array [0..CMaxValues-1] of Integer;
i, cnt: Integer;
begin
// Dynamic array using WordCount
Writeln('Enter values:');
Readln(s);
cnt := WordCount(s, StdWordDelims);
SetLength(darr, cnt); // Allocate room for values
for i := 0 to cnt - 1 do
Val(ExtractWord(i + 1, s, StdWordDelims), darr[i]);
for i in darr do
Writeln(i);
// Dynamic array usin EOLN
SetLength(darr, 0);
Writeln('Enter values:');
while not eoln do
begin
SetLength(darr, Length(darr) + 1); // Expand array for next value
Read(darr[High(darr)]);
end;
Readln; // Read <Enter> itself
for i in darr do
Writeln(i);
// Static array
cnt := 0;
Writeln('Enter values:');
while (not eoln) and (cnt < CMaxValues) do // Reads not more then CMaxValues values
begin
Read(sarr[cnt]);
Inc(cnt);
end;
Readln; // Read <Enter> itself
for i := 0 to cnt-1 do
Writeln(sarr[i]);
end.
Feel free to use one of them or provide your own :)
PS: Some readings:
Dynamic arrays
Val procedure
for-in loop

Pascal: how to pass arrays to subprograms?

I made a program that has a procedure with an array as one of it's parameters
program something ;
const someArray: array[1..4] of integer = (1, 2, 3, 4);
procedure name(someArray: array; a, n: integer);
begin
....
end;
begin
name(someArray, x, y)
end.
After compiling the program I get an error:
Fatal: Syntax error, OF expected but ; found (function name() is highlighted)
Why isn't this program working?
You need to declare your parameter properly, as an open array. You find the bounds of the array by using Low and High.
Here's a (useless but working) example:
program Sample;
Var x,y: Integer;
const
SomeArray: array[1..4] of Integer = (1, 2, 3, 4);
procedure Name(const AnArray: Array of Integer; const A, B: Integer);
var
OutOne, OutTwo, i: Integer;
begin
for i := Low(AnArray) to High(AnArray) do
begin
OutOne := AnArray[i] * A;
OutTwo := AnArray[i] * B;
WriteLn('One: ', OutOne, ' Two: ', OutTwo);
end;
end;
begin
//x and y have to be initialised before use
Name(SomeArray, x, y);
ReadLn;
end.
To complement Ken White's answer, in straight up (pre-open array) Pascal, array on its own in a parameter definition is unsupported.
Instead you need to declare a specific array type to do what you are trying to do here.
Here's what that could look like:
program something ;
type
TMyArray = array[1..4] of integer;
const someArray: TMyArray = (1, 2, 3, 4);
procedure name(someArray: TMyArray; a, n: integer);
begin
....
end;
begin
name(someArray, x, y)
end.

How many times does one number divide into another, and how much is left over?

I need an algorithm in Delphi to generate partitions for a specified integer value.
Example: for 13 if 5 is specified as the max value for partition it will give 5,5,3; if 4 is specified as max partition value the result should be 4,4,4,1, and so on.
It's simple enough to solve the problem using div and mod. Here's an example program that I don't think needs any further explanation:
program IntegerPartitions;
{$APPTYPE CONSOLE}
function Partitions(const Total, Part: Integer): TArray<Integer>;
var
Count: Integer;
Rem: Integer;
i: Integer;
begin
Assert(Total>0);
Assert(Part>0);
Count := Total div Part;
Rem := Total mod Part;
if Rem=0 then
SetLength(Result, Count)
else
SetLength(Result, Count+1);
for i := 0 to Count-1 do
Result[i] := Part;
if Rem<>0 then
Result[Count] := Rem;
end;
var
Value: Integer;
begin
for Value in Partitions(13, 5) do
Writeln(Value);
Readln;
end.

Resources