====================================================
Exercises
====================================================
This page contains three take-home exercises that reinforce the concepts
from Lecture 4. Each exercise asks you to **write code from scratch**
based on a specification -- no starter code is provided.
All files should be created inside your ``lecture4/`` workspace folder.
.. dropdown:: Exercise 1 -- Function Basics and Arguments
:icon: gear
:class-container: sd-border-primary
:class-title: sd-font-weight-bold
**Goal**
Demonstrate your understanding of function definitions, multiple return
values, default arguments, keyword arguments, ``*args``, and ``**kwargs``
by implementing a set of utility functions for robot configuration.
.. raw:: html
**Specification**
Create a file ``lecture4/robot_config.py`` that implements the following
functions. Each function must include type hints and a Google-style
docstring.
1. **``compute_distance``** -- Takes two tuples representing 2D
coordinates ``(x1, y1)`` and ``(x2, y2)``. Returns the Euclidean
distance rounded to 2 decimal places. Use the formula:
``sqrt((x2 - x1)**2 + (y2 - y1)**2)``. Import ``sqrt`` from the
``math`` module.
2. **``configure_motor``** -- Takes a required ``name`` (str) and optional
keyword arguments: ``speed`` (float, default ``1.0``), ``direction``
(str, default ``"forward"``), and ``enabled`` (bool, default ``True``).
Returns a dictionary with keys ``"name"``, ``"speed"``, ``"direction"``,
and ``"enabled"``.
3. **``compute_statistics``** -- Takes ``*args`` (any number of floats).
Returns a tuple of ``(count, total, average)`` rounded to 2 decimal
places. If no arguments are given, return ``(0, 0.0, 0.0)``.
4. **``build_command``** -- Takes a required ``action`` (str) and
``**kwargs`` for additional parameters. Returns a formatted string:
``"ACTION: | PARAMS: key1=val1, key2=val2, ..."``. If no
kwargs are given, the params section should say ``"none"``.
5. **``swap_coordinates``** -- Takes two tuples ``(x1, y1)`` and
``(x2, y2)`` and returns them swapped as a tuple of tuples:
``((x2, y2), (x1, y1))``. Demonstrate tuple unpacking when calling.
In the ``if __name__ == "__main__"`` block, call each function with
example arguments and print the results with labels.
**Expected output:**
.. code-block:: text
=== Distance ===
Distance from (0, 0) to (3, 4): 5.0
=== Motor Configuration ===
Default: {'name': 'left_wheel', 'speed': 1.0, 'direction': 'forward', 'enabled': True}
Custom: {'name': 'right_wheel', 'speed': 2.5, 'direction': 'reverse', 'enabled': False}
=== Statistics ===
Stats for (10.5, 20.3, 30.7, 40.1): (4, 101.6, 25.4)
Stats for (): (0, 0.0, 0.0)
=== Command Builder ===
build_command('move', x=10, y=20): ACTION: move | PARAMS: x=10, y=20
build_command('stop'): ACTION: stop | PARAMS: none
=== Swap ===
Before: a=(1, 2), b=(3, 4)
After: a=(3, 4), b=(1, 2)
.. raw:: html
**Deliverables**
- ``lecture4/robot_config.py``
- The program must run without errors and produce output matching the
expected format above.
.. dropdown:: Exercise 2 -- Scope and Pass-by-Assignment
:icon: gear
:class-container: sd-border-primary
:class-title: sd-font-weight-bold
**Goal**
Explore variable scoping (LEGB rule), the ``global`` and ``nonlocal``
keywords, and pass-by-assignment behavior with mutable and immutable
objects.
.. raw:: html
**Specification**
Create a file ``lecture4/scope_explorer.py`` that demonstrates each
concept below. Each task must print clearly labeled output showing
variable values before and after function calls.
1. **Local vs. Global** -- Define a global variable ``mode = "manual"``.
Write a function ``set_mode_local()`` that creates a local variable
``mode = "auto"`` and prints it. After calling the function, print the
global ``mode`` to show it is unchanged.
2. **The ``global`` keyword** -- Write a function ``set_mode_global()``
that uses the ``global`` keyword to modify the module-level ``mode``
variable to ``"autonomous"``. Print ``mode`` before and after the call.
3. **Enclosing scope with ``nonlocal``** -- Write a function
``make_counter()`` that defines a local variable ``count = 0`` and a
nested function ``increment()`` that uses ``nonlocal`` to increase
``count`` by 1 and returns it. Call ``increment()`` three times from
inside ``make_counter()`` and print the result each time.
4. **Pass-by-assignment with immutable** -- Write a function
``try_modify_int(x: int) -> None`` that adds 10 to ``x`` and prints
the result inside the function. Call it with ``value = 5`` and print
``value`` after the call to show it is unchanged.
5. **Pass-by-assignment with mutable** -- Write a function
``add_sensor(robot: dict, sensor: str) -> None`` that appends
``sensor`` to ``robot["sensors"]``. Call it and print the robot
dictionary before and after to show the in-place modification.
6. **Reassignment vs. mutation** -- Write a function
``reassign_list(data: list) -> None`` that reassigns ``data`` to a
new list ``[99, 99, 99]`` and prints it inside the function. Call it
with ``original = [1, 2, 3]`` and print ``original`` after the call
to show the original is unchanged.
**Expected output:**
.. code-block:: text
=== Task 1: Local vs Global ===
Inside set_mode_local(): auto
Global mode after call: manual
=== Task 2: global keyword ===
Before: manual
After set_mode_global(): autonomous
=== Task 3: nonlocal with make_counter ===
Increment 1: 1
Increment 2: 2
Increment 3: 3
=== Task 4: Immutable (int) ===
Inside function: 15
Outside after call: 5
=== Task 5: Mutable (dict) ===
Before: {'name': 'TurtleBot', 'sensors': ['lidar']}
After add_sensor: {'name': 'TurtleBot', 'sensors': ['lidar', 'camera']}
=== Task 6: Reassignment vs Mutation ===
Inside function: [99, 99, 99]
Outside after call: [1, 2, 3]
.. raw:: html
**Deliverables**
- ``lecture4/scope_explorer.py``
- The program must run without errors and produce output matching the
expected format above.
.. dropdown:: Exercise 3 -- Robot Toolkit
:icon: gear
:class-container: sd-border-primary
:class-title: sd-font-weight-bold
**Goal**
Combine all concepts from Lecture 4 -- function definition, arguments,
scope, pass-by-assignment, type hints, docstrings, and recursion -- to
build a toolkit of reusable functions for a robot navigation system.
.. raw:: html
**Specification**
Create a file ``lecture4/robot_toolkit.py`` that implements the following
functions. Every function must have type hints and a Google-style
docstring.
1. **``create_waypoint``** -- Takes ``name`` (str), ``x`` (float),
``y`` (float), and an optional ``priority`` (int, default ``0``).
Returns a dictionary with keys ``"name"``, ``"x"``, ``"y"``, and
``"priority"``.
2. **``compute_path_length``** -- Takes a list of waypoints (dicts from
``create_waypoint``). Computes the total Euclidean distance along the
path (from waypoint 0 to 1, 1 to 2, etc.). Returns the total
distance rounded to 2 decimal places. If the list has fewer than 2
waypoints, return ``0.0``.
3. **``sort_by_priority``** -- Takes a list of waypoints and returns a
**new** list sorted by priority in descending order (highest first).
The original list must not be modified. Use a loop (do NOT use the
built-in ``sorted()`` or ``list.sort()``). Implement a simple
selection sort.
4. **``format_waypoint``** -- Takes a waypoint dictionary and returns a
formatted string: ``"[P] @ (, )"``.
Example: ``"[P2] charger @ (5.0, 3.0)"``.
5. **``print_path``** -- Takes a list of waypoints and an optional
``label`` (str, default ``"Path"``). Prints the label, then each
waypoint using ``format_waypoint()``, then the total path length
using ``compute_path_length()``.
6. **``recursive_distance_sum``** -- Takes a list of float distances and
returns their sum using recursion (no loops, no ``sum()``). Base case:
empty list returns ``0.0``.
In the ``if __name__ == "__main__"`` block:
- Create at least 4 waypoints with different priorities.
- Store them in a list and print the path using ``print_path()``.
- Sort by priority and print the sorted path.
- Demonstrate ``recursive_distance_sum()`` with a list of segment
distances.
**Expected output:**
.. code-block:: text
=== Original Path ===
Path:
1. [P1] start @ (0.0, 0.0)
2. [P3] obstacle @ (3.0, 4.0)
3. [P0] waypoint_A @ (6.0, 4.0)
4. [P2] charger @ (6.0, 8.0)
Total distance: 12.0
=== Sorted by Priority ===
Priority Path:
1. [P3] obstacle @ (3.0, 4.0)
2. [P2] charger @ (6.0, 8.0)
3. [P1] start @ (0.0, 0.0)
4. [P0] waypoint_A @ (6.0, 4.0)
Total distance: 18.63
=== Recursive Distance Sum ===
Distances: [5.0, 3.0, 4.0]
Recursive sum: 12.0
.. raw:: html
**Deliverables**
- ``lecture4/robot_toolkit.py``
- The program must run without errors and produce output matching the
expected format above.
- All calculations must be computed dynamically (no hard-coded results).
- Every function must include type hints and a Google-style docstring.