The for…in statement loops over
successive values of an array:
var a: array[1..100] of integer; sum: integer = 0; … for v in a do sum := sum + v;
The preceding for loop is equivalent to
for i := low(a) to high(i) do sum := sum + a[i];
A record is a compound object that is composed of a set of fields. Each distinct record type defines the names and types of the fields that it contains. For example:
type
book = record
title: string;
author: string;
pages: integer;
end;
You can access a record's fields using the '.'
operator. For example:
var
b: book;
begin
b.title := 'war and peace';
b.author := 'leo tolstoy';
b.pages := 1440;
…
writeln('title = ', b.title);You can initialize a variable of any record type as you declare it:
var b: book = (title: 'war and peace'; author: 'leo tolstoy'; pages: 1440);
Here is a program that uses a record type to represent a playing card:
card = record
rank: 2 .. 14; // 11 = jack, 12 = queen, 13 = king, 14 = ace
suit: suit;
end;
The program uses an array of card to
hold a hand of cards. It draws a random 5-card hand and checks
whether it is a flush, i.e. whether all suits in the hand are
identical.
{$r+}
uses math, sysutils;
type
suit = (club, diamond, heart, spade);
card = record
rank: 2 .. 14; // 11 = jack, 12 = queen, 13 = king, 14 = ace
suit: suit;
end;
hand = array of card;
function describe(c: card): string;
var
ranks: array[11..14] of char = ('J', 'Q', 'K', 'A');
suits: array[suit] of string = ('♣', '♦', '♥', '♠');
s: string;
begin
if c.rank <= 10 then
s := intToStr(c.rank)
else s := ranks[c.rank];
describe := s + suits[c.suit];
end;
function randomCard(): card;
var
c: card;
begin
c.rank := random(13) + 2;
c.suit := suit(random(4));
randomCard := c;
end;
function isIn(c: card; h: hand): boolean;
var
i: integer;
begin
for i := low(h) to high(h) do
if (c.rank = h[i].rank) and (c.suit = h[i].suit)
then exit(true);
exit(false);
end;
function draw(n: integer): hand;
var
h: hand;
i: integer;
c: card;
begin
setLength(h, 0);
for i := 1 to n do
begin
repeat
c := randomCard();
until not isIn(c, h);
setLength(h, i);
h[i - 1] := c;
end;
draw := h;
end;
function isFlush(h: hand): boolean;
var
i: integer;
begin
for i := low(h) to high(h) - 1 do
if h[i].suit <> h[i + 1].suit then
exit(false);
exit(true);
end;
var
h: hand;
i: integer;
begin
randomize;
h := draw(5);
for i := 0 to high(h) do
write(describe(h[i]), ' ');
writeln;
writeln(isFlush(h));
end.In general, files hold either text or binary data. In a text file, all information is stored as Unicode characters. For example, an integer in a text file is stored as a series of characters representing decimal digits. In a binary file, an integer is stored as a series of bytes, each of which holds 8 consecutive bits of the integer’s binary representation.
The text type represents a text file:
var novel: text;
Call the assign function to assign a
filename to a file. You can then call reset
to open the file for reading, or rewrite
or append to open the file for writing.
Be warned that rewrite will truncate the
file, i.e. erase all its existing contents, if it already exists!
Call close to close the file when you
are done reading or writing.
begin assign(novel, ‘great_american’); rewrite(novel); writeln(novel, ‘It was a dark and stormy night.’); writeln(novel, ‘The wind howled through the treetops, and there was no moon in sight.’); close(novel);
The read, readln,
write and writeln
functions work with text files just as they do for standard input and
output.
The file type represents a file on
disk containing a particular type of binary data:
var f: file of integer; g: file of real;
Often a file will hold a compound type:
type
time = record
year, month, day: integer;
hour, minute, second: integer;
end;
var
timestamps: file of time;
Note, however, that a file cannot
contain data of any of these types:
string
dynamic arrays
any compound data type containing the above types
If you want to store character data in a binary file, you must use
a fixed-size character array, e.g. an array[1..100]
of char.
The assign, reset,
rewrite, append
and close functions work with binary
files just like with text files. The read
and write functions also work with
binary files, but you can only read or write values of the binary
file’s element type. Furthermore, readln
and writeln don’t work with binary
files since there are no newlines in such files.
Here is a program that simulates a random walk and writes a series of points to a binary file.
{$mode delphi}
type
point = record
x: integer;
y: integer;
end;
var
f: file of point;
n, i: integer;
p: point;
begin
randomize;
write('iterations? ');
readln(n);
assign(f, 'walk_data');
rewrite(f);
p.x := 0;
p.y := 0;
for i := 1 to n do
begin
write(f, p);
case random(4) of
0: p.x := p.x - 1;
1: p.x := p.x + 1;
2: p.y := p.y - 1;
3: p.y := p.y + 1;
end;
end;
close(f);
end.Here is a complementary program that reads the data written by the previous program, and prints out some statistics about the random walk.
{$mode delphi}
uses math;
type
point = record
x: integer;
y: integer;
end;
var
f: file of point;
time: integer = 0;
p: point;
distance: integer;
furthest: integer = 0;
furthest_time: integer = 0;
begin
assign(f, 'walk_data');
reset(f);
while not eof(f) do
begin
read(f, p);
time := time + 1;
if (p.x = 0) and (p.y = 0) then
writeln('hit origin at time ', time);
distance := abs(p.x) + abs(p.y);
if distance > furthest then
begin
furthest := distance;
furthest_time := time;
end;
end;
close(f);
writeln('total time = ', time);
writeln('furthest distance = ', furthest, ' at time ', furthest_time);
end.We are generally interested in characterizing a program or function’s running time as a function of its input size n.
Consider the following function, which returns the largest column sum in an (n x n) matrix.
uses math;
type matrix = array of array of integer;
// Return the largest column sum in an (n x n) matrix.
function largestColumnSum(a: matrix; n: integer): integer;
var
sum, largest, row, col: integer;
begin
largest := - MaxInt; // c1
for col := 0 to n - 1 do
begin
sum := 0; // c2
for row := 0 to n - 1 do
sum := sum + a[row, col]; // c3
largest := max(largest, sum); // c4
end;
exit(largest); // c5
end;
For the statements labelled c1 through
c5, let c1,
…c5
be the time costs of running each of these statements once. We assume
that the ci
are
constant.
For a given value of n,
c3 will run n2
times
c2 and c4
will run n times
c1
and
c5
will
run once
So the total running time of these lines is c3n2 + (c2 + c4)n + (c1 + c5). This equals an2 + bn + d for constants a, b, d.
Other statements in this function (e.g. the for loops themselves) will also each execute either n2 times, n times or 1 time. When we include their overhead, the constants a, b and d may change, but the function’s running time remains of the form an2 + bn + d.
In computer science, we generally ignore the constant factors and lower-order terms in a running time such as an2 + bn + d, and write it instead using big-O notation: an2 + bn + d = O(n^2). This indicates the order of growth of the running time, also known as the time complexity of the function or program.
f(n) = O(g(n)) means that f(n) is eventually bounded by some constant factor times g(n). We say that g(n) is an asymptotic upper bound for f(n).
More formally:
Definition. f(n) = O(g(n)) if there exist constants c, n0 > 0 such that 0 ≤ f(n) ≤ c ⋅ g(n) whenever n ≥ n0 .
For given functions f and g, it can be awkward to show that f(n) = O(g(n)) using the definition above. It is often much easier to work with limits, so the following theorem is useful.
Definition. A function f is eventually positive if there exists some n0 such that f(n) > 0 whenever n > n0 .
Theorem. If functions f and g are eventually positive, then f(n) = O(g(n)) iff `underset(n→oo)("lim sup ") f(n)/g(n) < oo`.
For any function h, if `lim_(n→oo) h(n) = k`, then `underset(n→oo)("lim sup ") h(n)` must equal k as well. Combining this observation with the preceding theorem, we see that we can prove that f(n) = O(g(n)) by showing that f(n) / g(n) converges to a constant.
For example, let us show that an2 + bn + d = O(n2) as stated above. We have
`lim_(n→oo)((a n^2 + b n + d) / n^2) = lim_(n→oo)(a + b / n + d / n^2) = a + lim_(n→oo)(b / n + d / n^2) = a`
The functions below are in increasing order of asymptotic complexity.
|
notation |
name |
|---|---|
|
O(1) |
constant |
|
O(log n) |
logarithmic |
|
O(n) |
linear |
|
O(n log n) |
log linear |
|
O(n2) |
quadratic |
|
O(n3) |
cubic |
|
O(nk) |
polynomial |
|
O(kn) |
exponential |
|
O(n!) |
factorial |
The Fundamental Theorem of Arithmetic states that each integer has a unique factorization as a product of prime numbers.
The simplest algorithm for factoring an integer is called trial division, in which we attempt to divide an integer n by successive integers i < n. Here is a program that performs naive trial division:
{$mode delphi}
var
n: integer;
i: integer;
begin
write('number to factor: ');
readln(n);
i := 2;
while i < n do
if n mod i = 0 then
begin
write(i, ' ');
n := n div i;
end
else
i := i + 1;
writeln(n);
end.This program works, but runs in time O(n) which is very inefficient for large n. We can make a couple of small changes to the program to make it more efficient:
{$mode delphi}
var
n: integer;
i: integer;
begin
write('number to factor: ');
readln(n);
i := 2;
while i <= sqrt(n) do
if n mod i = 0 then
begin
write(i, ' ');
n := n div i;
end
else
if i = 2 then
i := 3
else i := i + 2;
writeln(n);
end.The most important change here is that we loop only while i ≤ `sqrt(n)`. To see that this is sound, consider the following loop invariant: No prime factors of n are less than i.
The invariant is certainly true at the beginning of the loop, since there are no primes less than 2. In each loop iteration, if i | n then we replace n by n / i, which must also have no prime factors less than i. If i does not divide n, then we advance i to the next odd integer, and n still has no prime factors that are less than i.
So the loop preserves the invariant. When the loop terminates, i > `sqrt(n)`. Since n is not divisible by any j < i, it must also not be divisible by any j ≥ i > `sqrt(n)`, for then n would be divisible by n / j < `sqrt(n)`, a contradiction. So the final value of n must be prime.
This modified algorithm runs in time O(`sqrt(n)`). The modification to advance i only through odd integers makes the algorithm run twice as fast (which does not change its time complexity).