Final Exam: Concept-Only Refresh
Exam Strategy & Structure
Coverage and Weight
- Modules Covered: Modules 8 to 13, with some concepts from Module 7.
- Topic Weighting: C++ (~20%), Scheme (~40%), and Prolog (~40%). Budget study time accordingly.
Structure and Timing
- Format: Approximately 30 questions over 110 minutes, totaling 50 points.
- Pacing Heuristic: Aim for about 2 minutes per point to leave a buffer for challenging questions.
- Question Types: Around 30 points are autograded (like midterm quizzes), with the remaining 20 points being written responses (subject to change).
Approach and Materials
- Key Mental Models:
- C++: Focus on objects, lifetimes, and ownership.
- Scheme: Focus on expressions, lists, and recursion styles.
- Prolog: Focus on facts, rules, unification, and search order.
- Allowed Materials: One 3x5 inch notecard (double-sided, handwritten or printed). No separate scratch paper. A virtual scratchbox may be provided.
- Important Tip: Attempt all questions. Partial credit may be awarded for demonstrating logical thinking, even if the final answer isn't perfect.
Scheme Concepts
Lists and Recursion Styles
- Proper vs. Improper Lists: A proper list is a chain of pairs that ends in an empty list
'(). An improper list ends in a non-list value. Functions like cadr are only safe on proper lists of sufficient length.
- Non-Tail Recursion: Defers work until after the recursive call, which grows the call stack.
- Tail Recursion: The recursive call is the final action, allowing the compiler to reuse the stack frame (an optimization). Often preferred for clarity and efficiency in aggregation tasks.
- Common Pitfalls: Forgetting to quote data (e.g.,
'(1 2 3)), confusing symbols with numbers (pi vs. 'pi), and incorrectly assuming tail recursion is happening.
Higher-Order Functions
- Functions as Values: In Scheme, functions can be passed as arguments, returned from other functions, and stored in variables.
- Closures: A closure is a function that "remembers" the environment in which it was created, capturing variables from its surrounding scope.
- Key Patterns: Use higher-order functions like
map, filter, and fold (reduce) for clean, declarative patterns that avoid manual recursion.
Prolog Concepts
Core Mental Model
- Declarative Nature: You state facts (what is true) and rules (relationships), then ask queries. Prolog's engine searches for variable bindings that make the query true.
- Unification: The core mechanism. It's a powerful form of pattern matching that binds variables. A goal unifies with a fact/rule if their predicate name, arity, and arguments can all be matched.
- Search Strategy: Prolog uses a depth-first, left-to-right search. It backtracks automatically when a path fails to find a solution.
Execution Control
- The Cut (
!): An operator that commits Prolog to all choices made so far in a rule and prunes other branches from the search tree. It prevents backtracking past the cut, making a rule deterministic.
- Negation as Failure (
\+): A goal \+ G succeeds if Prolog fails to prove G. It's about provability, not mathematical negation.
C++ Concepts
Ownership and Polymorphism
- Smart Pointers: Prefer smart pointers (like
std::unique_ptr) over raw pointers to manage memory automatically and prevent leaks. They enforce clear ownership semantics.
- The Rule of Zero/Three/Five: If you manage a resource, you may need to define a destructor, copy constructor, and copy assignment operator (Rule of Three), plus move equivalents (Rule of Five). The Rule of Zero aims to use smart pointers and standard containers so that no manual resource management is needed.
- Composition vs. Inheritance: Use composition ("has-a") for ownership and inheritance ("is-a") for polymorphism.
- Virtual Functions: Dynamic dispatch (polymorphism) only occurs through base class pointers or references. Use a virtual destructor in a base class if it will be deleted through a base class pointer.
Constructors and Destructors
- Initialization Lists: Use constructor initialization lists to initialize members. This is more efficient and necessary for const members and references.
- Calling Base Constructors: A derived class constructor must call a base class constructor. The base class is always constructed before the derived class members are initialized.
- RAII (Resource Acquisition Is Initialization): A core C++ principle where resource lifetime is tied to object lifetime. Resources are acquired in the constructor and released in the destructor (e.g.,
std::unique_ptr).