Exercises: Generics
Part A: Conceptual Questions
A1. Explain the problem that generics solve. Why is writing separate TIntStack, TStringStack, and TExpenseStack classes undesirable?
A2. Compare generics with the Pointer-based TList approach. What are the three main advantages of generics?
A3. What is a type constraint? Why would you use T: class instead of leaving T unconstrained?
A4. Explain the difference between TFPGList<T> and TFPGObjectList<T>. When would you use each?
A5. What does "specialize" mean in the context of generics? Why does Free Pascal require the specialize keyword in {$mode objfpc}?
A6. Can a generic class have more than one type parameter? Give an example of when this would be useful.
A7. Why are generics described as having "zero runtime overhead"? What does the compiler do with a generic specialization?
Part B: Tracing and Prediction
B1. What is the output of this code?
type
generic TPair<TFirst, TSecond> = record
First: TFirst;
Second: TSecond;
end;
TIntStringPair = specialize TPair<Integer, String>;
var
P: TIntStringPair;
begin
P.First := 42;
P.Second := 'hello';
WriteLn(P.First, ' ', P.Second);
end.
B2. Will this code compile? If not, why?
type
generic TMinFinder<T> = class
function FindMin(A, B: T): T;
end;
function TMinFinder.FindMin(A, B: T): T;
begin
if A < B then
Result := A
else
Result := B;
end;
type
TIntMin = specialize TMinFinder<Integer>;
B3. What is the output?
uses fgl;
type
TIntList = specialize TFPGList<Integer>;
var
List: TIntList;
i: Integer;
begin
List := TIntList.Create;
try
List.Add(30);
List.Add(10);
List.Add(50);
List.Add(20);
List.Sort;
for i := 0 to List.Count - 1 do
Write(List[i], ' ');
WriteLn;
finally
List.Free;
end;
end.
Part C: Short Programming Exercises
C1. Generic Pair
Define a generic TPair<TKey, TValue> record with Key: TKey and Value: TValue fields. Write a generic function MakePair<TKey, TValue> that creates and returns a pair. Demonstrate with specialize TPair<String, Integer> to store name-age pairs.
C2. Generic Queue
Implement a generic TQueue<T> class with Enqueue(AValue: T), function Dequeue: T, function Peek: T, function IsEmpty: Boolean, and Count property. Use a dynamic array with head and tail indices. Test with integers and strings.
C3. Generic Minimum and Maximum
Write generic functions Min<T> and Max<T> that accept two values and a comparison function, and return the smaller or larger value. The comparison function should have type function(const A, B: T): Integer. Test with integers, strings, and dates.
C4. Generic Contains
Write a generic function Contains<T>(const Arr: array of T; const Target: T; Equals: function(const A, B: T): Boolean): Boolean that searches an array for a value using a caller-provided equality function. Test with an array of records.
C5. Generic TFPGMap Usage
Using TFPGMap<String, Integer>, write a program that reads words from a text file and counts the frequency of each word. Display the 10 most frequent words.
Part D: Design Challenges
D1. Generic Priority Queue
Implement a generic TPriorityQueue<T> class where each item has a priority (integer). Items with higher priority are dequeued first. Items with equal priority follow FIFO order. Use a dynamic array of records containing Value: T and Priority: Integer. Test with a hospital emergency room scenario: patients with severity 1 (critical) through 5 (minor) are added, and the queue always serves the most critical patient first.
D2. Generic Observable List
Combine generics with interfaces (from Chapter 18): create a generic TObservableList<T> that notifies registered observers whenever an item is added, removed, or changed. Define an IListObserver interface with OnItemAdded, OnItemRemoved, and OnItemChanged methods. Demonstrate with a list of expenses that automatically updates a budget tracker and a UI display whenever the list changes.
D3. Generic Repository Pattern
Design a generic TRepository<T: class> that provides CRUD operations (Create/Read/Update/Delete) for any class type. Store items in a TFPGObjectList<T>. Add methods: Add(Item: T), GetByIndex(Index: Integer): T, Remove(Index: Integer), Count: Integer, FindAll(Predicate: function(Item: T): Boolean): TFPGObjectList<T>. Demonstrate with TRepository<TExpense> and TRepository<TContact>.