I have the following object with an array of a record type:
type
TTimeMark = record
// many fields here
end;
TTimeMarks = array of TTimeMark;
TUserProfile = class(TObject)
TimeLine: TTimeMarks;
....
end;
In this list TUserProfile.TimeLine will be inserted items at run time. I don't know a method of inserting items other than increasing the lenghth of the array with one and then moving all the items a place down until I reach the desired possition. But in this array, the items are records with many fields, so, if I do TimeLine[I]:= TimeLine[I-1], all the data in the memory will be copied from one place to another (am I right ?), and this will take some time. Do you think should I use an array of pointers of that record, instead ? Or is there other fast method to do this ?
This is how I've done.. I used an array of pointers and I make some procedures to easy add, delete and move items:
TPointerArray = array of Pointer;
procedure PArrayInsert(var AArray: TPointerArray; Position: Integer; Count: Integer = 1);
var L, CTail: Integer;
begin
L:= Length(AArray);
if (Count = 0) or (Position > L) then Exit;
SetLength(AArray, L + Count);
CTail:= L - Position;
if CTail > 0 then
Move(AArray[Position], AArray[Position+Count], CTail * SizeOf(Pointer));
end;
procedure PArrayDelete(var AArray: TPointerArray; Position: Integer; Count: Integer = 1);
var L, CTail: Integer;
begin
L:= Length(AArray);
if (L = 0) or (Count = 0) or (Position >= L) or ((Position+Count) > L) then Exit;
CTail:= L - (Position + Count);
if CTail > 0 then
Move(AArray[Position+Count], AArray[Position], CTail * SizeOf(Pointer));
SetLength(AArray, L - Count);
end;
function PArrayMove(var AArray: TPointerArray; FromIndex, ToIndex: Integer; Count: Integer = 1): Boolean;
var L, Size, CT: Integer;
Buff: Pointer;
begin
Result:= False;
L:= High(AArray);
if (FromIndex > L) or (ToIndex > L+1) or
((ToIndex >= FromIndex) and (ToIndex <= (FromIndex+Count))) then Exit;
Size:= Count * SizeOf(Pointer);
GetMem(Buff, Size);
Move(AArray[FromIndex], Buff^, Size);
if FromIndex > ToIndex then begin
CT:= FromIndex - ToIndex;
Move(AArray[ToIndex], AArray[FromIndex+Count-CT], CT * SizeOf(Pointer));
Move(Buff^, AArray[ToIndex], Size);
end
else begin
CT:= ToIndex - FromIndex - Count;
Move(AArray[FromIndex+Count], AArray[FromIndex], CT * SizeOf(Pointer));
Move(Buff^, AArray[FromIndex+CT], Size);
end;
FreeMem(Buff);
Result:= True;
end;
Related
I need use loop from title,but I get incorrect value of result, under my code,please for suggestion, when i use a for everything is ok
const
licz_l = 5;
var
x, suma, min, max: longint;
tab: array [1..5] of longint;
begin
randomize();
suma := 0;
x := 0;
repeat
tab[x] := random(100);
suma := suma + tab[x];
write({'wylosowane liczby : ',}tab[x],';');
min := tab[1];
max := tab[1];
if (tab[x] < min) then
min := tab[x];
// max:=tab[1];
if (max < tab[x]) then
max := tab[x];
x := x + 1;
until x = licz_l;
writeln('');
writeln('srednia : ', suma / licz_l : 4 : 4);
writeln('min : ', min, #9, 'max : ', max);
end.
New code with second loop [for], but like I write, is a posibility to use only one loop[repeat until] to getvthe same effect?
const
licz_l = 5;
var
x, suma, min, max: longint;
tab: array [0..4] of longint;
begin
randomize();
suma := 0;
x := 0;
repeat
tab[x] := random(100);
suma := suma + tab[x];
write({'wylosowane liczby : ',}tab[x],';');
x := x + 1;
until x = licz_l;
min := tab[0];
max := tab[0];
for x := 0 to licz_l-1 do
begin
if (tab[x] < min) then
min := tab[x];
if (max < tab[x]) then
max := tab[x];
end;
writeln('');
writeln('srednia : ',suma/licz_l:4:4);
writeln('min : ',min,#9,'max : ',max);
end.
There are two possible ways to correct your first code (with a single loop).
Remove the lines within the loop: min := tab[1]; and max := tab[1];
Initialize min and max before the loop, with values that are,
on one hand > maximum value that random(100) may return, and
on other hand < minimum value that random(100) may return.
or
Assign min and max unconditionally with the first value, but only the first time your loop runs (if x = 0).
const
licz_l=5;
var
x,suma,min,max: longint;
tab: array [0..4] of longint;
begin
randomize();
suma:=0;
x:=0;
repeat
tab[x]:=random(100);
suma:=suma+tab[x];
write((*'wylosowane liczby : ',*)tab[x],';');
x:=x+1;
until x=licz_l;
min:=tab[0];
max:=tab[0];
for x:=0 to licz_l-1 do
begin
if (tab[x]<min) then
min:=tab[x];
if (max<tab[x]) then
max:=tab[x];
end;
writeln('');
writeln('srednia : ',suma/licz_l:4:4);
writeln('min : ',min,#9,'max : ',max);
end.
Or
const
licz_l = 5;
var
x, suma, min, max: longint;
tab: array [0..4] of longint;
begin
randomize();
suma := 0;
x := 0;
min := 99;
max := 0;
repeat
tab[x] := random(100);
suma := suma + tab[x];
write((*'wylosowane liczby : ',*)tab[x],';');
//min := tab[0];
//max := tab[0];
if (tab[x] < min) then
min := tab[x];
if (max < tab[x]) then
max := tab[x];
x := x + 1;
until x = licz_l;
writeln('');
writeln('srednia : ', suma / licz_l : 4 : 4);
writeln('min : ', min, #9, 'max : ', max);
end.
I wanted to multiply 2 matrices in codesys. I have implemented the code in the structured text language. However I am not able to generate the results correctly. Below is the logic which i am trying to implement.
Initialisation in codesys:
PROGRAM POU
VAR
a: ARRAY [1..5,1..2] OF INT:= [1,2,3,4,5,6,7,8,9,1];
b: ARRAY [1..2,1..5] OF INT := [1,2,3,4,5,6,7,8,9,1];
r1: INT:= 5; // no. of rows in a;
c1: INT:=2; // no. of columns in a;
r2: INT:= 2; //no. Of rows in b;
c2: INT:= 5; //no. of columns in b;
i: INT;
j: INT ;
k: INT;
z: INT:=2;
result: ARRAY [1..5,1..5] OF INT:= 0;
END_VAR
Program logic:
FOR i:=1 TO r1 DO
FOR j:=1 TO c2 DO
FOR k:=1 TO z DO
result[i,j]:= result[i,j]+(a[i,k]*b[k,j]);
END_FOR
END_FOR
END_FOR
What may be the reason for not getting the desired output?
The only obvious problem I see is, if you want to initialize an array with a specific value, don't do result: ARRAY [1..5, 1..5] OF INT := 0 as you would should get an error C0032: Cannot convert type 'BIT' to type 'ARRAY [1..5, 1..5] OF INT', instead do this: result: ARRAY [1..5, 1..5] OF INT := [ 25(0) ], although in codesys arrays Elements to which no initialization value is assigned explicitly are initialized internally with the default value of the basic data type, which in case of an INT is 0.
Also, to avoid errors, instad of hardcoding the boundaries I'd recomment to instad use the built in LOWER_BOUND an UPPER_BOUND functions.
Here's an example generic function for Matrix multiplication based on the code in your question:
METHOD IntMatrixProduct : BOOL
VAR_IN_OUT
A: ARRAY [*, *] OF INT;
B: ARRAY [*, *] OF INT;
C: ARRAY [*, *] OF INT;
END_VAR
VAR_INPUT
zero_C: BOOL := TRUE;
END_VAR
VAR
al1: DINT := LOWER_BOUND(A, 1);
au1: DINT := UPPER_BOUND(A, 1);
al2: DINT := LOWER_BOUND(A, 2);
au2: DINT := UPPER_BOUND(A, 2);
bl1: DINT := LOWER_BOUND(B, 1);
bu1: DINT := UPPER_BOUND(B, 1);
bl2: DINT := LOWER_BOUND(B, 2);
bu2: DINT := UPPER_BOUND(B, 2);
cl1: DINT := LOWER_BOUND(C, 1);
cu1: DINT := UPPER_BOUND(C, 1);
cl2: DINT := LOWER_BOUND(C, 2);
cu2: DINT := UPPER_BOUND(C, 2);
height: DINT := au1 - al1;
width: DINT := bu2 - bl2;
common: DINT := au2 - al2;
i, j, k: DINT;
END_VAR
IF (common <> bu1 - bl1 // Width of A != Height of B
OR_ELSE cu1 - cl1 <> height // Height of C != Height of A
OR_ELSE cu2 - cl2 <> width) THEN // Width of C != Width of B
IntMatrixProduct := FALSE; // Error!
RETURN;
END_IF
// Zero C
IF (zero_C) THEN
FOR i := 0 TO height DO
FOR j := 0 TO width DO
result[cl1 + i, cl2 + j] := 0;
END_FOR
END_FOR
END_IF
// Calcutale A*B
FOR i := 0 TO height DO
FOR j := 0 TO width DO
FOR k := 0 TO common DO
result[cl1 + i, cl2 + j] := result[cl1 + i, cl2 + j] + (a[al1 + i, al2 + k] * b[bl1 + k, bl2 + j]);
END_FOR
END_FOR
END_FOR
IntMatrixProduct := TRUE; // Success
You can also try importing the code using PLCOpen from here.
PS. Here's the result I get after running the above function:
I am new in Dafny and I try to figure out why this doesn't work. What I want to do is to insert 2 values in my arrays, priorities, respectively values.
I have the following code:
class Queue<V> {
var size: int;
ghost var capacity: int;
var priorities: array<int>;
var values: array<V>;
predicate Valid()
reads this
{
0 <= size <= capacity &&
capacity == priorities.Length &&
capacity == values.Length
}
constructor(aCapacity: int, defaultValue: V)
requires aCapacity >= 0
ensures Valid()
{
size := 0;
values := new V[aCapacity](i => defaultValue);
priorities := new int[aCapacity];
capacity := aCapacity;
}
method insertValues(priority: int, value: V)
modifies this.values, this.priorities, this
requires Valid()
requires 0 <= size < capacity // here is the problem
ensures Valid()
ensures capacity == values.Length && capacity == priorities.Length
{
this.values[size] := value;
this.priorities[size] := priority;
size := size + 1;
}
}
method Main() {
var queue := new Queue<int>(10, 0);
queue.insertValues(1, 10);
queue.insertValues(2, 11);
}
But when I try to tests my method insertValues in Main it says that
call may violate context's modifies clause
A precondition for this call might not hold.
and the precondition is 0 <= size < capacity. Thank you in advance.
The issue is that Dafny analyzes each method in isolation, using only the specifications of the other methods. See the Dafny FAQ for more information.
You need to add more postconditions to guarantee that certain things aren't changed by insertValues, and you need to also add more postconditions to the constructor so that callers know the initial state. Here is a version that verifies:
class Queue<V> {
var size: int;
ghost var capacity: int;
var priorities: array<int>;
var values: array<V>;
predicate Valid()
reads this
{
0 <= size <= capacity &&
capacity == priorities.Length &&
capacity == values.Length
}
constructor(aCapacity: int, defaultValue: V)
requires aCapacity >= 0
ensures Valid()
ensures fresh(priorities) && fresh(values)
ensures size == 0 && capacity == aCapacity
{
size := 0;
values := new V[aCapacity](i => defaultValue);
priorities := new int[aCapacity];
capacity := aCapacity;
}
method insertValues(priority: int, value: V)
modifies this.values, this.priorities, this
requires Valid()
requires 0 <= size < capacity // here is the problem
ensures Valid()
ensures capacity == old(capacity) && size == old(size) + 1 && values == old(values) && priorities == old(priorities)
{
this.values[size] := value;
this.priorities[size] := priority;
size := size + 1;
}
}
method Main() {
var queue := new Queue<int>(10, 0);
queue.insertValues(1, 10);
queue.insertValues(2, 11);
}
I have the program to print out each word in a file along with its frequency but I am trying to adjust it so that it also outputs which lines the word appears on.
I know I could create a Word_List type with
words: Word_Array; --unique words
Num_Words: Natural := 0; -- How many unique words seen so far
curr_line: Natural := 1;
but I wanted to see if I could execute this without doing that and by adjusting my existing code.
procedure v3 is
type Word is record
wlen: Natural := 45; -- Length of the word
count: Natural := 0; -- Total number of occurrences of this word
s: Unbounded_String;
value: Unbounded_String;
LineNo: Natural := 0;
end record;
type IntArray is array (1 .. 10) of Natural;
type Word_Array is array (Natural range <>) of Word;
--Sorting--
function "+" (S : String) return Unbounded_String renames To_Unbounded_String;
function "+" (S : Natural) return Unbounded_String renames To_Unbounded_String;
function "<" (L, R : Word) return Boolean is
begin
return L.s < R.s;
end "<";
function "=" (L, R : Word) return Boolean is
begin
return L.s = R.s;
end "=";
procedure Sort is new Ada.Containers.Generic_Array_Sort (Natural, Word, Word_Array);
----
num_words : Integer;
procedure get_words(wl: out Word_Array) is
Input : File_Type;
begin
num_words := 0;
Open (File => Input,
Mode => In_File,
Name => "input.txt");
loop
declare
s : String := Get_Line(Input);
found: Boolean := false;
word : Unbounded_String;
index : Integer := 0;
word_len:Integer := 0;
empty : Unbounded_String;
space : Unbounded_String;
begin
-- process the contents of Line here.
index := 1;
for I in s'Range loop
word_len := word_len + 1;
if (index = s'Length or s(I) = ' ') then
if (s(I) /= ' ') then
word := word & s(I);
end if;
space := space & s(I);
found := false;
for i in 1 .. num_words loop
if word = wl(i).s then
wl(i).count := wl(i).count + 1;
found := true;
end if;
exit when found;
end loop;
if not found and word /= empty then -- Add word to list
num_words := num_words + 1;
wl(num_words).s := word;
wl(num_words).wlen := word_len;
wl(num_words).count := 1;
end if;
word := empty;
word_len := 0;
else
word := word & s(I);
end if;
index := index + 1;
end loop;
end;
end loop;
exception
when End_Error =>
if Is_Open(Input) then
Close (Input);
end if;
END Get_Words;
Data : Word_Array(1 .. 1000);
Output : File_Type;
begin
get_words(Data);
Sort (Data);
--Create(F, Ada.Text_IO.Out_File, "output.txt");
Create (File => Output,
Mode => Out_File,
Name => "output.txt");
for I in Data'Range loop
if Data(I).wlen /= 45 then
Put_Line(Output, To_String(Data(I).s & Integer'Image(Data(I).count)));
end if;
end loop;
close(Output);
end v3;
So if the file had:
Hello Hello
Hello World
World World
World
My current output is
Hello 3
World 4
I want my new output to be
Hello 1-2 wc: 3
World 2-3-4 wc: 4
I need to sort arrays by integer field, with 1-n sorted at the beginning and zeros last:
0,0,3,1,2 -> 1,2,3,0,0
I don't know how to sort it in one go, so I tried in 2 sorts, but it doesn't produce correct results:
It does put zeros at the end, but it messes up 1-n ordered items:
0,0,3,1,2 -> (first sort) 0,0,1,2,3 -> (second sort) 2,3,1,0,0
procedure TForm2.Button1Click(Sender: TObject);
var
Arr: TArray<integer>;
begin
SetLength(Arr, 5);
Arr[0] := 0;
Arr[1] := 0;
Arr[2] := 3;
Arr[3] := 1;
Arr[4] := 2;
// First sort: Sort 1-n
TArray.Sort<integer>(Arr, TComparer<integer>.Construct(function(const Left, Right: integer): Integer
begin
if Left < Right then
Result := -1
else if Left > Right then
Result := 1
else
Result := 0;
end
));
// Second sort: Put zeros at the end
TArray.Sort<integer>(Arr, TComparer<integer>.Construct(function(const Left, Right: integer): Integer
begin
if (Left = 0) and (right>0) then
Result := 1
else
Result := 0;
end
));
end;
Is there a way to do this kind of sort in one, single Sort operation?
Try this, the point being to deal with the special 0 cases first in the if-then-else ladder, before the ordinary cases.
TArray.Sort<integer>(Arr, TComparer<integer>.Construct(function(const Left, Right: integer): Integer
begin
if (Left = 0) and (Right = 0) then
Result := 0
else if (Left = 0) then
Result := 1
else if (Right = 0) then
Result := -1
else if (Left < Right) then
Result := -1
else if (Left > Right) then
Result := 1
else
Result := 0;
end
));
A brief testing shows it works OK.
Just fix your compare function so that it will treat 0 as being larger than anything.
Untested:
TArray.Sort<integer>(Arr, TComparer<integer>.Construct(function(const Left, Right: integer): Integer
begin
if Left = Right then
Result := 0
else if ((Left <> 0) and (Left < Right)) or (Right = 0) then
Result := -1
else
Result := 1;
end
));