Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@
"prerequisites": [],
"difficulty": 2
},
{
"slug": "pangram",
"name": "Pangram",
"uuid": "39b6d6e7-5e32-482f-949e-96f6323456bd",
"practices": [],
"prerequisites": [],
"difficulty": 2
},
{
"slug": "trinary",
"name": "Trinary",
Expand Down Expand Up @@ -276,14 +284,6 @@
"prerequisites": [],
"difficulty": 3
},
{
"slug": "pangram",
"name": "Pangram",
"uuid": "39b6d6e7-5e32-482f-949e-96f6323456bd",
"practices": [],
"prerequisites": [],
"difficulty": 3
},
{
"slug": "perfect-numbers",
"name": "Perfect Numbers",
Expand Down
49 changes: 49 additions & 0 deletions exercises/practice/pangram/.approaches/array/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Using an Array of Boolean

```pascal
function IsPangram(const sentence : string) : boolean;
var
seen : Array [0..25] of Boolean;
i : integer;
j : integer;
c : char;
begin
for i := 0 to 25 do
seen[i] := false;
j := 1;
while j <= Length(sentence) do
begin
c := LowerCase(sentence[j]);
if c in ['a'..'z'] then
seen[ord(c) - ord('a')] := true;
Inc(j);
end;
result := true;
for i := 0 to 25 do
if not seen[i] then
begin
result := false;
break;
end;
end;
```

`Array [0..25] of Boolean` declares a fixed-size array with indices `0` through `25`, one slot per letter of the alphabet.
Pascal allows any ordinal type as an array index range, so the bounds are written directly as integer literals.

[`LowerCase`][lowercase] converts a single character to lowercase.
The single-character overload lives in the `System` unit, which is always available without a `uses` clause.

`ord(c) - ord('a')` maps a lowercase letter to its zero-based index: `'a'` → `0`, `'b'` → `1`, …, `'z'` → `25`.
The `in ['a'..'z']` guard ensures only letters reach the array, so the index is always in range.

[`Inc(j)`][inc] advances the loop variable.
Writing `j := 1` before the loop and `Inc(j)` at the bottom of the body is the idiomatic Pascal `while` loop pattern, equivalent to a `for` loop with an explicit start and end but allowing the index to be used after the loop exits.

Once the sentence has been scanned, a second loop checks every slot.
As soon as an unset entry is found the function assigns `false` to [`result`][result] and uses [`break`][break] to leave the loop early.

[lowercase]: https://www.freepascal.org/docs-html/3.2.2/rtl/system/lowercase.html
[inc]: https://www.freepascal.org/docs-html/3.2.2/rtl/system/inc.html
[result]: https://www.freepascal.org/docs-html/3.2.2/ref/refse94.html
[break]: https://www.freepascal.org/docs-html/3.2.2/rtl/system/break.html
8 changes: 8 additions & 0 deletions exercises/practice/pangram/.approaches/array/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
j := 1;
while j <= Length(sentence) do
begin
c := LowerCase(sentence[j]);
if c in ['a'..'z'] then
seen[ord(c) - ord('a')] := true;
Inc(j);
end;
36 changes: 36 additions & 0 deletions exercises/practice/pangram/.approaches/bitfield/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Using a Bit Field

```pascal
function IsPangram(const sentence : string) : boolean;
var
letters : longword;
c : char;
begin
letters := 0;
for c in sentence do
if c in ['a'..'z'] then
letters := letters or (1 shl (ord(c) - ord('a')))
else if c in ['A'..'Z'] then
letters := letters or (1 shl (ord(c) - ord('A')));
result := letters = $3FFFFFF;
end;
```

A [`longword`][longword] is a 32-bit unsigned integer.
We use one bit per letter: bit 0 for `a`, bit 1 for `b`, …, bit 25 for `z`.

[`shl`][logical] is Pascal's left-shift operator.
`1 shl (ord(c) - ord('a'))` produces a mask with exactly one bit set — the bit corresponding to the letter `c`.
The [`or`][logical] operator sets that bit in `letters`.

The `in` operator tests membership in a set literal: `c in ['a'..'z']` returns `true` when `c` is a lowercase letter.
Handling upper and lower case separately avoids a call to any case-conversion function.

`$3FFFFFF` is a hexadecimal literal equal to 67,108,863, which has its lowest 26 bits set — one for each letter of the alphabet.
When `letters = $3FFFFFF`, every letter has been seen.

The [`for c in sentence`][for-in] loop iterates directly over the characters of the string without index arithmetic.

[longword]: https://www.freepascal.org/docs-html/3.2.2/ref/refsu4.html
[logical]: https://www.freepascal.org/docs-html/3.2.2/ref/refsu45.html
[for-in]: https://www.freepascal.org/docs-html/3.2.2/ref/refsu59.html
7 changes: 7 additions & 0 deletions exercises/practice/pangram/.approaches/bitfield/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
letters := 0;
for c in sentence do
if c in ['a'..'z'] then
letters := letters or (1 shl (ord(c) - ord('a')))
else if c in ['A'..'Z'] then
letters := letters or (1 shl (ord(c) - ord('A')));
result := letters = $3FFFFFF;
29 changes: 29 additions & 0 deletions exercises/practice/pangram/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"introduction": {
"authors": ["keiravillekode"],
"contributors": []
},
"approaches": [
{
"uuid": "879bf49d-8221-45ad-bee6-d01475303d68",
"slug": "set",
"title": "Using a Set of Char",
"blurb": "Collect seen characters into a set of char and test subset membership.",
"authors": ["keiravillekode"]
},
{
"uuid": "59807da6-df1d-430b-a55c-fcdd8dad3516",
"slug": "bitfield",
"title": "Using a Bit Field",
"blurb": "Track seen letters with a longword bit field and compare to a 26-bit mask.",
"authors": ["keiravillekode"]
},
{
"uuid": "0db168c1-23a8-4829-974d-2bba65f3f76a",
"slug": "array",
"title": "Using an Array of Boolean",
"blurb": "Track seen letters in a fixed Array [0..25] of Boolean indexed by character arithmetic.",
"authors": ["keiravillekode"]
}
]
}
91 changes: 91 additions & 0 deletions exercises/practice/pangram/.approaches/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Introduction

There are multiple ways to solve the Pangram exercise.
Among them are:

- Using a `Set of Char`
- Using a Bit Field
- Using an Array of Boolean


## Approach: Using a `Set of Char`

Build a `set of char` from the sentence, then test whether `['a'..'z']` is a subset of it.

```pascal
function IsPangram(const sentence : string) : boolean;
var
found : set of char;
i : integer;
begin
found := [];
for i := low(sentence) to high(sentence) do
Include(found, LowerCase(sentence[i]));
result := ['a'..'z'] <= found;
end;
```

For more information, check the [set approach][approach-set].


## Approach: Using a Bit Field

Use a `longword` to track which of the 26 letters have been seen, then compare against `$3FFFFFF`.

```pascal
function IsPangram(const sentence : string) : boolean;
var
letters : longword;
c : char;
begin
letters := 0;
for c in sentence do
if c in ['a'..'z'] then
letters := letters or (1 shl (ord(c) - ord('a')))
else if c in ['A'..'Z'] then
letters := letters or (1 shl (ord(c) - ord('A')));
result := letters = $3FFFFFF;
end;
```

For more information, check the [bitfield approach][approach-bitfield].


## Approach: Using an Array of Boolean

Track seen letters in a fixed `Array [0..25] of Boolean`, using `ord(c) - ord('a')` to index each slot.

```pascal
function IsPangram(const sentence : string) : boolean;
var
seen : Array [0..25] of Boolean;
i : integer;
j : integer;
c : char;
begin
for i := 0 to 25 do
seen[i] := false;
j := 1;
while j <= Length(sentence) do
begin
c := LowerCase(sentence[j]);
if c in ['a'..'z'] then
seen[ord(c) - ord('a')] := true;
Inc(j);
end;
result := true;
for i := 0 to 25 do
if not seen[i] then
begin
result := false;
break;
end;
end;
```

For more information, check the [array approach][approach-array].


[approach-set]: https://exercism.org/tracks/free-pascal/exercises/pangram/approaches/set
[approach-bitfield]: https://exercism.org/tracks/free-pascal/exercises/pangram/approaches/bitfield
[approach-array]: https://exercism.org/tracks/free-pascal/exercises/pangram/approaches/array
38 changes: 38 additions & 0 deletions exercises/practice/pangram/.approaches/set/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Using a `Set of Char`

```pascal
function IsPangram(const sentence : string) : boolean;
var
found : set of char;
i : integer;
begin
found := [];
for i := low(sentence) to high(sentence) do
Include(found, LowerCase(sentence[i]));
result := ['a'..'z'] <= found;
end;
```

Pascal's [`set of`][set-of] type is a built-in ordered collection of values from an ordinal type.
`set of char` can hold any subset of the 256 possible `char` values, stored internally as a bit array.

[`Include`][include] adds an element to a set in-place — equivalent to `found := found + [c]` but potentially faster.

[`LowerCase`][lowercase] converts a character to lowercase.

When working with arrays and strings, it is easy to make [off-by-one] errors.
Indices for an array range from `0` to `length(arr) - 1` inclusive.
Indices for a string range from `1` to `length(str)` inclusive.

A convenient way to iterate over all valid indices of an array or string is to use [`low()`][low] to [`high()`][high], which automatically gives the correct bounds.

The expression `['a'..'z'] <= found` uses the subset operator `<=`.
A set `A` is a subset of set `B` if every element of `A` is also in `B`.
When `found` contains all 26 letters, this returns `true`.

[off-by-one]: https://en.wikipedia.org/wiki/Off-by-one_error
[set-of]: https://www.freepascal.org/docs-html/3.2.2/ref/refsu16.html
[include]: https://www.freepascal.org/docs-html/3.2.2/rtl/system/include.html
[lowercase]: https://www.freepascal.org/docs-html/3.2.2/rtl/system/lowercase.html
[low]: https://www.freepascal.org/docs-html/3.2.2/rtl/system/low.html
[high]: https://www.freepascal.org/docs-html/3.2.2/rtl/system/high.html
4 changes: 4 additions & 0 deletions exercises/practice/pangram/.approaches/set/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
found := [];
for i := low(sentence) to high(sentence) do
Include(found, LowerCase(sentence[i]));
result := ['a'..'z'] <= found;