Find the smallest and largest values in a list in a single pass. Seeding the running bounds from the first element avoids sentinel values; the trace shows exactly when each bound tightens.

By hand

Seed lo and hi from values[0] so no sentinel (0, float('inf')) is needed. Then scan the rest: lower the floor when a new minimum appears, raise the ceiling when a new maximum appears.

naive.py
Replay: real traced execution (multi-file project)
values = [5, 2, 8, 1, 9, 3, 7, 4]
lo = values[0]
hi = values[0]
for v in values[1:]:
    if v < lo:
        lo = v
    if v > hi:
        hi = v
print('RESULT:', (lo, hi))
  1. values ← [5, 2, 8, 1, 9, 3, 7, 4]

    1values = [5, 2, 8, 1, 9, 3, 7, 4]2lo = values[0]
    values this step[5, 2, 8, 1, 9, 3, 7, 4]values
  2. lo ← 5

    1values = [5, 2, 8, 1, 9, 3, 7, 4]2lo = values[0]3hi = values[0]
    values this step5lo
  3. hi ← 5

    2lo = values[0]3hi = values[0]4for v in values[1:]:
    values this step5hi
  4. v ← 2

    3hi = values[0]4for v in values[1:]:5    if v < lo:
    values this step2v
  5. if v < lo:

    4for v in values[1:]:5    if v < lo:6        lo = v
  6. lo ← 2

    5if v < lo:6    lo = v7if v > hi:
    values this step5 2lo
  7. if v > hi:

    6    lo = v7if v > hi:8    hi = v
  8. v ← 8

    3hi = values[0]4for v in values[1:]:5    if v < lo:
    values this step2 8v
  9. if v < lo:

    4for v in values[1:]:5    if v < lo:6        lo = v
  10. if v > hi:

    6    lo = v7if v > hi:8    hi = v
  11. hi ← 8

    7    if v > hi:8        hi = v9print('RESULT:', (lo, hi))
    values this step5 8hi
  12. v ← 1

    3hi = values[0]4for v in values[1:]:5    if v < lo:
    values this step8 1v
  13. if v < lo:

    4for v in values[1:]:5    if v < lo:6        lo = v
  14. lo ← 1

    5if v < lo:6    lo = v7if v > hi:
    values this step2 1lo
  15. if v > hi:

    6    lo = v7if v > hi:8    hi = v
  16. v ← 9

    3hi = values[0]4for v in values[1:]:5    if v < lo:
    values this step1 9v
  17. if v < lo:

    4for v in values[1:]:5    if v < lo:6        lo = v
  18. if v > hi:

    6    lo = v7if v > hi:8    hi = v
  19. hi ← 9

    7    if v > hi:8        hi = v9print('RESULT:', (lo, hi))
    values this step8 9hi
  20. v ← 3

    3hi = values[0]4for v in values[1:]:5    if v < lo:
    values this step9 3v
  21. if v < lo:

    4for v in values[1:]:5    if v < lo:6        lo = v
  22. if v > hi:

    6    lo = v7if v > hi:8    hi = v
  23. v ← 7

    3hi = values[0]4for v in values[1:]:5    if v < lo:
    values this step3 7v
  24. if v < lo:

    4for v in values[1:]:5    if v < lo:6        lo = v
  25. if v > hi:

    6    lo = v7if v > hi:8    hi = v
  26. v ← 4

    3hi = values[0]4for v in values[1:]:5    if v < lo:
    values this step7 4v
  27. if v < lo:

    4for v in values[1:]:5    if v < lo:6        lo = v
  28. if v > hi:

    6    lo = v7if v > hi:8    hi = v
  29. for v in values[1:]:

    3hi = values[0]4for v in values[1:]:5    if v < lo:
  30. stdout ← RESULT: (1, 9)

    8        hi = v9print('RESULT:', (lo, hi))
    values this stepRESULT: (1, 9)stdout

The Pythonic way

The built-ins min and max each walk the list once internally in C. Two calls over eight values is negligible, and the intent is unambiguous.

library.py
values = [5, 2, 8, 1, 9, 3, 7, 4]
lo = min(values)
hi = max(values)
print('RESULT:', (lo, hi))
RESULT: (1, 9)

Implementation notes

  • Seeding from values[0] is correct for any non-empty list of any comparable type; a sentinel like 0 or float('inf') would require assumptions about the data range.
  • In this run lo tightens twice (5→2→1) and hi rises twice (5→8→9); both progressions are visible in the trace.