Your complete guide to C++, Scheme, and Prolog.
This is the fundamental design choice in OOP. Getting this right is crucial.
"is-a" (Inheritance): Used when a class is a more specific version of another. A `Book` is-a `Publication`. This is for code reuse and polymorphism. Modeled with `public` inheritance.
class Publication { ... }; class Book : public Publication { ... }; // A Book "is-a" Publication
"has-a" (Containment/Composition): Used when a class is composed of, or contains, other objects. A `Car` has-a `Engine`. Modeled by including an object as a member variable.
class Engine { ... }; class Car { private: Engine myEngine; // A Car "has-a" Engine };
Polymorphism allows derived class objects to be treated as base class objects. The `virtual` keyword is the key to enabling this at runtime.
A function declared `virtual` in a base class can be **overridden** in a derived class. C++ will determine which version to call at runtime based on the object's actual type.
class Publication { public: virtual void display() { ... } // Can be overridden }; class Book : public Publication { public: void display() override { ... } // Overrides the base version }; // Usage Publication* pub = new Book(); pub->display(); // Calls Book's display() at runtime
Critical Exam Fact: If you intend to `delete` a derived object through a base class pointer, you **MUST** declare the base class destructor as `virtual`. This ensures the derived destructor is called, preventing memory leaks.
class Publication { public: virtual ~Publication() {} // Virtual destructor is essential! };
The `try`/`catch` mechanism provides a safe way to handle runtime errors.
A `throw` statement is matched to a `catch` block based on the **data type** of the thrown value, not its name or content.
try { if (errorCondition) { throw std::runtime_error("A specific error occurred!"); } } catch (const std::runtime_error& e) { // This block executes because the data types match. std::cerr << e.what() << std::endl; }
Scheme focuses on composing functions and evaluating expressions. The goal is to avoid side effects (modifying state outside a function's scope).
Prefix Notation: The operator always comes first inside the parentheses. Example: `(+ 1 2)`.
`let` is Local, `define` is Global: Use `let` to create temporary, local names. Use `define` for top-level, global functions and constants. A `let` is just syntactic sugar for an immediately-invoked `lambda`.
; let form for local scope (let ((x 5) (y 10)) (+ x y)) ; => 15 ; Equivalent lambda form ((lambda (x y) (+ x y)) 5 10)
The fundamental data structure is the pair (`cons` cell), which is used to build lists.
A list is a chain of pairs ending in the empty list `'()`. The empty list is the **only** list that is not a pair.
; car: first element. cdr: rest of the list. (car '(1 2 3)) ; => 1 (cdr '(1 2 3)) ; => '(2 3) ; Building lists (cons 1 '(2 3)) ; => '(1 2 3) (adds one element) (list 1 2 3) ; => '(1 2 3) (creates from elements) (append '(1 2) '(3 4)) ; => '(1 2 3 4) (joins lists)
Recursion is the primary way to iterate in Scheme. Higher-order functions operate on other functions, enabling powerful abstractions.
Tail Recursion is Scheme's version of an efficient loop. The recursive call is the final action, allowing the compiler to optimize it and prevent stack overflow. This often requires an "accumulator" parameter.
; Tail-recursive factorial with an accumulator (define (factorial-tail n acc) (if (= n 0) acc (factorial-tail (- n 1) (* n acc))))
`map` and `filter` are essential higher-order functions. `map` applies a function to each element. `filter` keeps elements that satisfy a predicate.
; Double every element in a list (map (lambda (x) (* x 2)) '(1 2 3)) ; => '(2 4 6) ; Keep only the even numbers (filter even? '(1 2 3 4)) ; => '(2 4)
You describe the world using **facts** and **rules**. You then ask questions (**queries**), and Prolog's search engine finds answers through unification and backtracking.
Fact: A statement of truth. Must end with a period. `likes(bill, chocolate).`
Rule: An inference. `head :- body.` means "head is true if body is true". A comma `,` in the body means AND. A semicolon `;` means OR.
Query: A question asked to the database. Starts with `?-`.
% Facts male(luke). parent(luke, leia). parent(luke, han). % Rule father_of(X, Y) :- male(X), parent(X, Y). % Query ?- father_of(luke, Child). Child = leia ; Child = han.
The `[H|T]` syntax is the primary tool for list manipulation and recursion. It deconstructs a list into its first element (`Head`) and the rest of the list (`Tail`).
This syntax is fundamental for writing recursive rules that process lists one element at a time.
% Rule to check for list membership member(X, [X|_]). % X is a member if it is the Head. member(X, [_|T]) :- member(X, T). % X is a member if it's in the Tail. % The underscore _ is the anonymous "don't care" variable.
Prolog's execution is a search process. You can influence this search.
Backtracking: When a goal fails, Prolog automatically goes back to the last choice it made and tries an alternative path.
The Cut (`!`): The `cut` is a special goal that always succeeds once but **commits** Prolog to all choices made so far in the current rule. It prunes the search tree, preventing backtracking past it. This can improve efficiency but may also eliminate valid solutions.
% Without a cut, this would find both cat and dog mammal(X) :- warm_blooded(X), four_legs(X). % With a cut, it stops after finding the first solution. first_mammal(X) :- warm_blooded(X), four_legs(X), !. ?- first_mammal(M). M = cat. % Prolog stops and does not backtrack to find dog.