Exercise 1: A Join With No Matches At All — Possible Solution ==================================================================== THE TEST ------------------------------ lonely_customers = [(50, 'Nobody')] lonely_orders = [(500, 999, 30)] # references customer 999, which doesn't exist here nl_empty = nested_loop_join(lonely_customers, 0, lonely_orders, 1) hj_empty = hash_join(lonely_customers, 0, lonely_orders, 1) RESULT ------------------------------ nested-loop result: [] hash join result: [] Both functions return an empty list. Neither raises an exception, returns None, or requires the caller to check for "no matches" as a special case before using the result. WHY NESTED-LOOP HANDLES THIS WITHOUT ANY SPECIAL CASE ------------------------------ nested_loop_join()'s own inner if-check (l[left_key_idx] == r[right_key_idx]) simply never evaluates to True for this input -- 50 != 999 for the one pair actually compared. The results list starts empty and nothing ever appends to it. There's no separate "did we find anything" branch; an empty result is just what naturally falls out of a loop that never matched anything. WHY HASH JOIN HANDLES THIS WITHOUT ANY SPECIAL CASE EITHER ------------------------------ hash_join()'s own build step puts key 50 into the hash table (from lonely_customers). The probe step looks up key 999 (from lonely_orders) via hash_table.get(key, []) -- the second argument to .get() is exactly what makes this safe: a key that was never inserted returns an empty list by default, rather than raising a KeyError. The inner for build_row in hash_table.get(key, []): loop then simply doesn't execute at all, since there's nothing to iterate over. WHY THIS MATTERS AS A CORRECTNESS PROPERTY ------------------------------ A join implementation that crashed, returned None, or otherwise treated "zero matches" as an unusual case would be a genuine liability -- a customer with no orders at all is one of the single most ordinary situations a real e-commerce database has to represent correctly, not an edge case to work around. Both algorithms treating "nothing matched" as just an ordinary, unremarkable outcome of the same code path used for every other input is exactly the right behavior. WHY THIS WORKS AS AN ANSWER ------------------------------ Explicitly constructing input with zero possible matches, rather than just trusting that an implementation "probably" handles it, confirms directly that neither join algorithm has an implicit assumption that at least one match exists somewhere in the data.