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
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.
So my friend gave me this task where the sum of squares of positive numbers must be calculated using recursion.
Conditions - The input will be a string with space separated numbers
This is what I've come so far but this shows a runtime error.
Here is the full error https://ideone.com/53oOjN
package main
import(
'fmt',
'strings',
'strconv'
)
var n int = 4
var sum_of_squares int = 0
func sumOfSquares(strArray []string, iterate int) int{
number, _ := strconv.Atoi(strArray[iterate])
if number > 0 {
sum_of_squares += number*number
}
if iterate == n {
return 0 // just to end the recursion
}
return sumOfSquares(strArray, iterate+1)
}
func main() {
str := "1 2 3 4"
strArray := strings.Fields(str)
result := sumOfSquares(strArray, 0)
fmt.Println(sum_of_squares, result)
}
The rule of thumb in recursion is termination condition. It should exist and it should exist in the right place.
func sumOfSquares(strArray []string, iterate int) int{
if iterate >= len(strArray) {
return sum_of_squares
}
number, _ := strconv.Atoi(strArray[iterate]) //TODO: handle err here
sum_of_squares += number*number
return sumOfSquares(strArray, iterate+1)
}
Just for you information: canonical recursion should not save it's state into global fields. I would suggest using following function signature.
func sumOfSquares(strArray []string, iterate, currentSum int) int{
//...
return sumOfSquares(strArray, iterate+1, sum_of_squares)
}
So that you don't need to store sum_of_squares somewhere. You will just pass it to next function invocation.
package main
import (
"fmt"
"strconv"
"strings"
)
var n int
func sumOfSquares(strArray []string, iterate int) int {
number, _ := strconv.Atoi(strArray[iterate])
if iterate == n {
return number * number
}
return ((number * number) + sumOfSquares(strArray, iterate+1))
}
func main() {
str := "1 2 3 4"
strArray := strings.Fields(str)
n = len(strArray) - 1
result := sumOfSquares(strArray, 0)
fmt.Println(result)
}
Indexing starts from 0, so decrease the length by one.
As #peterSO have pointed out, if strings contain unusual characters, it doesn't work, I didn't post the right answer for getting input because you seem to be beginner, but you can read the input, like this instead.
var inp []byte
var loc int
inp, _ = ioutil.ReadFile(fileName)
//add \n so that we don't end up running out of bounds,
//if last byte is integer.
inp = append(inp, '\n')
func scanInt() (res int) {
if loc < len(inp) {
for ; inp[loc] < 48 || inp[loc] > 57; loc++ {
}
for ; inp[loc] > 47 && inp[loc] < 58; loc++ {
res = res<<3 + res<<1 + (int(inp[loc]) - 48)
}
}
return
}
This is faster and scans integers only, and skips all other unusual characters.
I like to keep it simple. I have some few if conditions as well, but hope you like it.
func sumOfSquares(numArr []string) int {
i, err := strconv.Atoi(numArr[0])
rest := numArr[1:]
//Error checking
if err != nil {
fmt.Println(err)
os.Exit(1)
return 0
}
square := i * i
// negative & last number
if i < 0 && len(rest) == 0 {
return square
}
// negative & not last number
if i < 0 && len(rest) > 0 {
return sumOfSquares(rest)
}
// last man standing
if i >= 0 && len(rest) == 0 {
return square
}
return square + sumOfSquares(rest)
}
DEMO : https://play.golang.org/p/WWYxKbvzanJ
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;
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
));
Pascal apparently has string concatenation, but does it have general concatenation for any type of array?
It does not but you can make your own function i coded this in 5 minutes...i am shure there is a better method but i don't have any idea right now.
type
TBArray = array of byte;
function ConcArray(arr1,arr2:TBArray):TBArray;
begin
SetLength(Result,Length(arr1) + Length(arr2));
ZeroMemory(#Result[0],Length(arr1) + Length(arr2));
CopyMemory(#Result[0],#arr1[0],Length(arr1));
CopyMemory(#Result[Length(arr1)],#arr2[0],Length(arr2));
end;
How about this? If you need to concatenate in many places, the + operator looks pretty nice IMHO.
program ConCatTest;
type
SomeRec = record
s: AnsiString;
i: Longint;
end;
SomeArr = array of SomeRec;
operator + (a, b: SomeArr) c: SomeArr;
var
i: Longint;
begin
SetLength(c, Length(a) + Length(b));
for i := 0 to High(a) do
c[i] := a[i];
for i := 0 to High(b) do
c[i + Length(a)] := b[i];
end;
var
a1, a2, a3: SomeArr;
i: Longint;
begin
SetLength(a1, 3);
SetLength(a2, 2);
a1[0].s := 'a';
a1[0].i := 0;
a1[1].s := 'a';
a1[1].i := 1;
a1[2].s := 'a';
a1[2].i := 2;
a2[0].s := 'b';
a2[0].i := 0;
a2[1].s := 'b';
a2[1].i := 1;
a3 := a1 + a2;
for i := 0 to High(a3) do
WriteLn(a3[i].s, a3[i].i);
ReadLn;
end.