From 4d33b3929cc2d4c023a389b926a2db41bcaf64f6 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Thu, 24 Jul 2025 11:22:58 -0700 Subject: [PATCH 01/11] WIP --- better-code/book.toml | 2 +- better-code/src/SUMMARY.md | 1 + better-code/src/chapter-3-algorithms.md | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 better-code/src/chapter-3-algorithms.md diff --git a/better-code/book.toml b/better-code/book.toml index 5bd576a..fa377ff 100644 --- a/better-code/book.toml +++ b/better-code/book.toml @@ -11,7 +11,7 @@ build-dir = "book" [output.html] git-repository-url = "https://github.com/stlab/better-code" -edit-url-template = "https://github.com/stlab/better-code/edit/main/better-code/src/{path}" +edit-url-template = "https://github.com/stlab/better-code/edit/main/better-code/{path}" site-url = "/better-code/" cname = "stlab.github.io" diff --git a/better-code/src/SUMMARY.md b/better-code/src/SUMMARY.md index fff4974..ff54a97 100644 --- a/better-code/src/SUMMARY.md +++ b/better-code/src/SUMMARY.md @@ -1,3 +1,4 @@ # Summary - [Introduction](./chapter-1-introduction.md) +- [Chapter 3: Algorithms](./chapter-3-algorithms.md) diff --git a/better-code/src/chapter-3-algorithms.md b/better-code/src/chapter-3-algorithms.md new file mode 100644 index 0000000..c157892 --- /dev/null +++ b/better-code/src/chapter-3-algorithms.md @@ -0,0 +1,21 @@ +# Algorithms + +>_
No raw loops.
_ + +The most important part of software development is that the purpose of the code +is to compute _something_. Algorithms are an abstraction of computation, and +every program is an algorithm. It is easy to be distracted by class hierarchies, +software architecture, design patterns, etc. Such things are helpful only in so +far as they aid in implementing a correct and efficient algorithm. + +**Algorithm** (n.): _a process or set of rules to be followed in calculations or other problem-solving operations, especially by a computer_ (New Oxford American Dictionary). + +## Closed-Form Algorithms +## Sequential Algorithms +## Composition +## Algorithmic Forms +### In-Place +### Functional +#### Eager +#### Lazy +## Efficiency From 919ef7e7ad19fd24cce75a99caa918163bc96cd7 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Mon, 20 Oct 2025 12:15:44 -0700 Subject: [PATCH 02/11] Working on algorithms. --- .vscode/settings.json | 15 +++++++------- better-code/README.md | 2 +- better-code/js/mathjax-config.js | 10 +++++++++ better-code/src/chapter-3-algorithms.md | 27 ++++++++++++++++++++++++- better-code/src/test.swift | 16 +++++++++++++++ 5 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 better-code/js/mathjax-config.js create mode 100644 better-code/src/test.swift diff --git a/.vscode/settings.json b/.vscode/settings.json index c03803f..109333f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,11 @@ { - "grammarly.selectors": [ - { - "language": "markdown", - "scheme": "file" - } - ], "rewrap.autoWrap.enabled": true, - "rewrap.wrappingColumn": 80 + "rewrap.wrappingColumn": 80, + "cSpell.words": [ + "footgun", + "Gitter", + "irreflexivity", + "preorder" + ], + "cmake.ignoreCMakeListsMissing": true } diff --git a/better-code/README.md b/better-code/README.md index 1125d25..6c036c1 100644 --- a/better-code/README.md +++ b/better-code/README.md @@ -16,7 +16,7 @@ curl https://sh.rustup.rs -sSf | sh **Windows:** Download the installer from [here](https://win.rustup.rs/). -### Install mdBook +### Install (or update) mdBook ```bash cargo install mdbook diff --git a/better-code/js/mathjax-config.js b/better-code/js/mathjax-config.js new file mode 100644 index 0000000..aff0409 --- /dev/null +++ b/better-code/js/mathjax-config.js @@ -0,0 +1,10 @@ +/* Must load before MathJax runs */ +if (window.MathJax && window.MathJax.Hub) { + MathJax.Hub.Config({ + "HTML-CSS": { + availableFonts: [], // do not try local STIX + webFont: "TeX", // download TeX webfonts + imageFont: null + } + }); +} diff --git a/better-code/src/chapter-3-algorithms.md b/better-code/src/chapter-3-algorithms.md index c157892..7a05cc7 100644 --- a/better-code/src/chapter-3-algorithms.md +++ b/better-code/src/chapter-3-algorithms.md @@ -8,7 +8,32 @@ every program is an algorithm. It is easy to be distracted by class hierarchies, software architecture, design patterns, etc. Such things are helpful only in so far as they aid in implementing a correct and efficient algorithm. -**Algorithm** (n.): _a process or set of rules to be followed in calculations or other problem-solving operations, especially by a computer_ (New Oxford American Dictionary). +**Algorithm** (n.): _a process or set of rules to be followed in calculations or +other problem-solving operations, especially by a computer_ (New Oxford American +Dictionary). + +```swift +{{#include test.swift:2:5}} +``` + +```swift +func partitionPoint(_ array: [T], _ predicate: (T) -> Bool) -> Int { + var left = 0 + var right = array.count + while left < right { + let mid = (left + right) / 2 + if predicate(array[mid]) { + left = mid + 1 + } else { + right = mid + } + } + return left +} + +let array = [1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 10] +let index = partitionPoint(array, { $0 <= 5 }) // lower bound? +``` ## Closed-Form Algorithms ## Sequential Algorithms diff --git a/better-code/src/test.swift b/better-code/src/test.swift new file mode 100644 index 0000000..1a7790a --- /dev/null +++ b/better-code/src/test.swift @@ -0,0 +1,16 @@ +func partitionPoint(_ array: [T], _ predicate: (T) -> Bool) -> Int { + var left = 0 + var right = array.count + while left < right { + let mid = (left + right) / 2 + if predicate(array[mid]) { + left = mid + 1 + } else { + right = mid + } + } + return left +} + +let array = [1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 10] +let index = partitionPoint(array, { $0 <= 5 }) // lower bound? From efe5840e510f6b275440948581553692d7587888 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Mon, 26 Jan 2026 13:31:10 -0800 Subject: [PATCH 03/11] Update chapter-3-algorithms.md --- better-code/src/chapter-3-algorithms.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/better-code/src/chapter-3-algorithms.md b/better-code/src/chapter-3-algorithms.md index 7a05cc7..80b8103 100644 --- a/better-code/src/chapter-3-algorithms.md +++ b/better-code/src/chapter-3-algorithms.md @@ -2,16 +2,30 @@ >_
No raw loops.
_ -The most important part of software development is that the purpose of the code -is to compute _something_. Algorithms are an abstraction of computation, and -every program is an algorithm. It is easy to be distracted by class hierarchies, -software architecture, design patterns, etc. Such things are helpful only in so -far as they aid in implementing a correct and efficient algorithm. +The most important part of software development is to compute _something_. +Algorithms are an abstraction of computation, and every program is an algorithm. +It is easy to be distracted by class hierarchies, software architecture, design +patterns, etc. Such things are helpful only in so far as they aid in +implementing a correct and efficient algorithm. **Algorithm** (n.): _a process or set of rules to be followed in calculations or other problem-solving operations, especially by a computer_ (New Oxford American Dictionary). +When designing a program, we start with a statement of what the program will do, +and we refine that statement until we reach a level of detail sufficient to +begin the implementation. The process is iterative, as we discover +details that inform and change the overall design. + +For each component of the design we will have a statement of what the component +is or does. Components that do something are operations and the statement of +what they do form the basis for the contract. + +Let's take an example, if we were writing a program to create images by placing +shapes on a canvas we would discover the need to erase a shape. If we have +decided for efficiency to store our shapes in an `Array` (we will discuss data +structures more in the Data Structures chapter). + ```swift {{#include test.swift:2:5}} ``` From 588048c555500722e51d15c63c34bf23fbfa4cd8 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Thu, 29 Jan 2026 17:25:45 -0800 Subject: [PATCH 04/11] Update algorithms chapter and VSCode config --- .vscode/settings.json | 3 +- .vscode/tasks.json | 18 ++ better-code/src/2026-01-29-15-52-36.png | Bin 0 -> 10047 bytes better-code/src/SUMMARY.md | 2 +- better-code/src/chapter-3-algorithms.md | 60 ----- better-code/src/chapter-4-algorithms.md | 290 ++++++++++++++++++++++++ 6 files changed, 311 insertions(+), 62 deletions(-) create mode 100644 .vscode/tasks.json create mode 100644 better-code/src/2026-01-29-15-52-36.png delete mode 100644 better-code/src/chapter-3-algorithms.md create mode 100644 better-code/src/chapter-4-algorithms.md diff --git a/.vscode/settings.json b/.vscode/settings.json index 109333f..af4a2f0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,8 +4,9 @@ "cSpell.words": [ "footgun", "Gitter", + "inout", "irreflexivity", "preorder" ], "cmake.ignoreCMakeListsMissing": true -} +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..6bdd308 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,18 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Open in Notepad", + "type": "shell", + "command": "notepad", + "args": [ + "${file}" + ], + "presentation": { + "reveal": "never", + "panel": "shared" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/better-code/src/2026-01-29-15-52-36.png b/better-code/src/2026-01-29-15-52-36.png new file mode 100644 index 0000000000000000000000000000000000000000..3373e8ed96da82851ef3acd271957f190da04391 GIT binary patch literal 10047 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 literal 0 HcmV?d00001 diff --git a/better-code/src/SUMMARY.md b/better-code/src/SUMMARY.md index b54b14b..629ee31 100644 --- a/better-code/src/SUMMARY.md +++ b/better-code/src/SUMMARY.md @@ -2,4 +2,4 @@ - [Introduction](./chapter-1-introduction.md) - [Contracts](./chapter-2-contracts.md) -- [Algorithms](./chapter-3-algorithms.md) +- [Algorithms](./chapter-4-algorithms.md) diff --git a/better-code/src/chapter-3-algorithms.md b/better-code/src/chapter-3-algorithms.md deleted file mode 100644 index 80b8103..0000000 --- a/better-code/src/chapter-3-algorithms.md +++ /dev/null @@ -1,60 +0,0 @@ -# Algorithms - ->_
No raw loops.
_ - -The most important part of software development is to compute _something_. -Algorithms are an abstraction of computation, and every program is an algorithm. -It is easy to be distracted by class hierarchies, software architecture, design -patterns, etc. Such things are helpful only in so far as they aid in -implementing a correct and efficient algorithm. - -**Algorithm** (n.): _a process or set of rules to be followed in calculations or -other problem-solving operations, especially by a computer_ (New Oxford American -Dictionary). - -When designing a program, we start with a statement of what the program will do, -and we refine that statement until we reach a level of detail sufficient to -begin the implementation. The process is iterative, as we discover -details that inform and change the overall design. - -For each component of the design we will have a statement of what the component -is or does. Components that do something are operations and the statement of -what they do form the basis for the contract. - -Let's take an example, if we were writing a program to create images by placing -shapes on a canvas we would discover the need to erase a shape. If we have -decided for efficiency to store our shapes in an `Array` (we will discuss data -structures more in the Data Structures chapter). - -```swift -{{#include test.swift:2:5}} -``` - -```swift -func partitionPoint(_ array: [T], _ predicate: (T) -> Bool) -> Int { - var left = 0 - var right = array.count - while left < right { - let mid = (left + right) / 2 - if predicate(array[mid]) { - left = mid + 1 - } else { - right = mid - } - } - return left -} - -let array = [1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 10] -let index = partitionPoint(array, { $0 <= 5 }) // lower bound? -``` - -## Closed-Form Algorithms -## Sequential Algorithms -## Composition -## Algorithmic Forms -### In-Place -### Functional -#### Eager -#### Lazy -## Efficiency diff --git a/better-code/src/chapter-4-algorithms.md b/better-code/src/chapter-4-algorithms.md new file mode 100644 index 0000000..dd2d3d5 --- /dev/null +++ b/better-code/src/chapter-4-algorithms.md @@ -0,0 +1,290 @@ +# Algorithms + +>_
No raw loops.
_ + +The most important part of software development is computing _something_. +Algorithms are an abstraction of computation, and every program is an algorithm. +It is easy to be distracted by class hierarchies, software architecture, design +patterns, etc. Such things are helpful only insofar as they aid in +implementing a correct and efficient algorithm. + +**Algorithm** (n.): _a process or set of rules to be followed in calculations or +other problem-solving operations, especially by a computer_ (New Oxford American +Dictionary). + +When designing a program, we start with a statement of what the program will do, +and we refine that statement until we reach a level of detail sufficient to +begin the implementation. The process is iterative as we discover details that +inform and change the overall design. + +For each component of the design, we will have a statement of what the component +is or does. Components that _do_ something are operations, and the statement of +what they do form the basis for the operations contract. + +Consider a simple example. Suppose we are building a small drawing tool and want +to remove a selected shape from an array. The naive implementation is a loop +that scans for the shape and erases it. + +```swift +/// Removes the selected shape. +func removeSelected(shapes: inout [Shape]) { + for i in 0.. + +Once we encapsulate the loops, the operations become simple. + + +A rotation +expresses “move this range here.” A stable partition expresses “collect the +elements that satisfy this predicate.” A slide is a composition of rotations. A +gather is a composition of stable partitions. + +The loops are still there. They are no longer our problem. + +Real programs rarely need a single loop. They need several: one loop to find +something, another to remove it, another to insert it somewhere else, and perhaps another to repair the structure afterward. + +Each loop is simple in isolation. Together they form a fragile tangle of index +adjustments, off‑by‑one corrections, and implicit assumptions about the state of +the data. + +A loop is a mechanism. It tells the computer *how* to step, but it does not tell +the reader *what* is being computed. + +The problem is not the loops themselves. The problem is that they are exposed. +They are raw. They are unencapsulated. + +Encapsulation is the difference between a loop and an algorithm. A loop is a +mechanism. An algorithm is a named, reusable composition that hides the +mechanism and exposes the intent. + +A raw loop is a loop that appears directly in application code. It exposes +mechanics instead of intent. It forces the reader to reason about control flow +instead of the operation being performed. Raw loops are unencapsulated loops. + +When I say “no raw loops,” I do not mean “no loops.” Every algorithm contains +loops. The problem is not iteration; the problem is exposure. A raw loop is a +loop that appears in application code, unencapsulated, forcing the reader to +reason about mechanics instead of intent. Real operations often require several +loops—one to find something, another to remove it, another to insert it +somewhere else. Left raw, these loops accumulate into brittle, low-level code. +Encapsulated as algorithms, they become simple, composable building blocks. + +We do not avoid loops. We avoid exposing them. + +Sorting is another encapsulated loop. It hides a complex algorithm behind a +single name. Once the data is sorted, other algorithms—binary search, +lower bound—become possible. Structure enables composition. + + +# From here down are notes and experiments + +# Algorithms +## From Mechanism to Intent +A loop is a mechanism a named algorithm is a statement of intent. +## Discovering Algorithms +Discover and not invent - learning to decompose problems and compose algorithms. +## Generalizing Algorithms +`slide` and `gather`? Very loosely cover generics but the principals of generic programming will come in with the type chapter. +## Algorithmic Tradeoffs and Complexity +## Algorithms Form a Vocabulary +## Structure as Algorithmic Leverage +I want to cover an example of structuring data to make other algorithms more +efficient - maybe sort and lower-bound or the binary-search "heap" like +structure with lower-bound to show constant time efficient gains. This section +will bridge to the data structure chapter. + + +# Algorithms +
No raw loops.
+ +Software exists to compute. Everything else—types, classes, modules, architecture—is scaffolding around that central act. An algorithm is the abstraction of that act: a precise, repeatable composition of operations that transforms input into output. Every program is an algorithm, and every algorithm is a composition. + +Raw loops hide this. A loop is a mechanism, not an idea. It tells the computer *how* to step, but it does not tell the reader *what* is being computed. This chapter is about replacing mechanisms with ideas—about expressing computation in terms of named, reusable algorithms rather than ad‑hoc control flow. + +## From Mechanism to Intent + +Consider a simple example. Suppose we are building a small drawing tool and want to remove a selected shape from an array. The naive implementation is a loop that scans for the shape and erases it. It works, but the loop obscures the intent: “remove this element.” + +Extend the requirement: the user can now select multiple shapes. The naive loop becomes more complicated. Removing one element shifts the indices of the remaining ones, so the loop must compensate. Reversing the iteration order avoids the index drift, but the algorithm is still quadratic. The code is longer, more fragile, and no clearer about what it is trying to do. + +This is the cost of working at the level of mechanisms. The problem is not the example; the problem is the approach. + +## Algorithms Capture Patterns + +A better approach is to express the operation directly: “keep the shapes that are not selected.” This is a partitioning problem. A half‑stable partition moves the elements we want to keep to the front and the ones we want to discard to the back. After that, removing the tail is trivial. + +This is exactly what `delete_if` does. It is a named, reusable algorithm that captures a common pattern of computation. It replaces a fragile loop with a clear statement of intent. + +The important point is not the specific algorithm. It is the shift in thinking: from manipulating indices to expressing a transformation. + +## Discovering Algorithms Through Composition + +As programs grow, we encounter new operations that are awkward to express with raw loops. Suppose we want to move a selected element to a new position. One way is to copy it out, erase it, and insert it back. This works, but it decomposes the operation into low‑level steps. The code describes the mechanics, not the intent. + +A better expression of the intent is rotation. A rotation shifts a block of elements while preserving their order. Moving one element is a rotation of a block of size one. Moving several consecutive elements is the same pattern. The algorithm is the idea: “rotate this range so that these elements end up here.” + +Rotation is a more powerful building block than a hand‑rolled loop. It composes cleanly with other algorithms, and it generalizes. + +## Generalizing Patterns: `slide` and `gather` + +Once we recognize rotation as the underlying pattern, we can name the operation we actually want: sliding a range of elements to a new position. A `slide` algorithm expresses this directly. It takes a range and a target position and performs the appropriate rotation. The code is short, the intent is clear, and the algorithm is reusable. + +Sometimes the elements we want to move are not consecutive. We can still express the operation in terms of existing algorithms. A stable partition can collect (“gather”) the selected elements into a contiguous block. Once gathered, a single slide moves them as a unit. This yields a `gather` algorithm: a composition of two stable partitions and a slide. + +None of these algorithms depend on shapes. They depend only on iterators and predicates. They are generic. They capture patterns of computation that appear in many domains. + +This is the power of algorithmic thinking: we discover general solutions by abstracting from specific problems. + +## Algorithmic Tradeoffs and Guarantees + +For many problems, there is more than one algorithm. Each has different tradeoffs. Some algorithms are greedy; some are lazy. Some operate in place; others require additional storage. Some require only forward iteration; others rely on random‑access. + +Rotation is a good example. With forward iterators, rotation requires repeated swaps and is linear in the size of the range. With random‑access iterators, we can rotate more efficiently by reversing subranges. The stronger the guarantees on the types, the more efficient the algorithm can be. + +Choosing the right algorithm requires understanding these tradeoffs. This is part of the craft of programming: matching the problem to the algorithmic vocabulary. + +## Algorithms Form a Vocabulary + +Algorithms are not limited to rearranging elements in a container. Numeric algorithms, graph algorithms, geometric algorithms, and string algorithms are all compositions of operations. Even `swap` is an algorithm: a minimal, correct, reusable composition. + +As our vocabulary grows, we can express more ideas directly. We write less code, and the code we write is clearer. We stop thinking in terms of loops and start thinking in terms of transformations. + +## Structure as Algorithmic Leverage + +Sometimes the best way to improve an algorithm is to change the shape of the data. Consider searching for a value in a range. A linear search is simple but takes time proportional to the size of the range. If we sort the data, we impose a structure: if `i < j`, then `a[i] <= a[j]`. This structure enables binary search, which finds a value in logarithmic time. + +The algorithm did not change; the data did. By structuring the data, we made a more efficient algorithm possible. + +This is a general principle: data structure is algorithmic leverage. The representation of the data determines which algorithms are available and how efficient they can be. + +## Bridge to the Next Chapter + +Sorting is the simplest example of structuring data to enable efficient algorithms. More sophisticated structures—trees, heaps, hash tables—provide even more leverage. They make certain operations fast by organizing data in ways that algorithms can exploit. + +The next chapter explores these structures. Here, we have focused on the algorithms themselves: named, reusable compositions that express intent, improve clarity, and enable efficient computation. + + +### + + +```swift +{{#include test.swift:2:5}} +``` + +```swift +func partitionPoint(_ array: [T], _ predicate: (T) -> Bool) -> Int { + var left = 0 + var right = array.count + while left < right { + let mid = (left + right) / 2 + if predicate(array[mid]) { + left = mid + 1 + } else { + right = mid + } + } + return left +} + +let array = [1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 10] +let index = partitionPoint(array, { $0 <= 5 }) // lower bound? +``` + +### Algorithm categories + +I want to mention categories but and the importance of developing taxonomies to +assist you in finding an algorithm to solve a problem. + +- Permutations +- Closed-Form Algorithms +- Sequential Algorithms +- Composition +- Algorithmic Forms + - In-Place + - Functional + - Eager + - Lazy + +## Efficiency From fa9a97e7deb453c4b8d2f5a164bba33663ca51eb Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Mon, 2 Feb 2026 12:54:49 -0800 Subject: [PATCH 05/11] Enhance algorithms chapter and update VSCode settings with new language options --- .vscode/settings.json | 4 +++- better-code/src/chapter-4-algorithms.md | 13 ++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index af4a2f0..aa85728 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,9 @@ "Gitter", "inout", "irreflexivity", - "preorder" + "preorder", + "subranges", + "unencapsulated" ], "cmake.ignoreCMakeListsMissing": true } \ No newline at end of file diff --git a/better-code/src/chapter-4-algorithms.md b/better-code/src/chapter-4-algorithms.md index dd2d3d5..d0b105a 100644 --- a/better-code/src/chapter-4-algorithms.md +++ b/better-code/src/chapter-4-algorithms.md @@ -21,7 +21,7 @@ For each component of the design, we will have a statement of what the component is or does. Components that _do_ something are operations, and the statement of what they do form the basis for the operations contract. -Consider a simple example. Suppose we are building a small drawing tool and want +Consider a simple example. Suppose we are building a drawing tool and want to remove a selected shape from an array. The naive implementation is a loop that scans for the shape and erases it. @@ -37,7 +37,9 @@ func removeSelected(shapes: inout [Shape]) { } ``` -Extend the requirement: the user can now select multiple shapes. The loop becomes more complicated. Removing one element shifts the indices of the remaining ones, so the loop must compensate. +Extend the requirement: the user can now select multiple shapes. The loop +becomes more complicated. Removing one element shifts the indices of the +remaining ones, so the code must compensate. ```swift /// Remove all selected shapes. (BAD) @@ -87,9 +89,10 @@ perhaps another to repair the structure afterward. There is an algorithm known as half-stable partition which can be used to remove the elements in linear time. The trick is to walk two indices forward so the -first index finds the next element to remove, and the second one points to -subsequent element to keep. Then we swap the elements and proceed end. Then we -can remove all the elements at the end. +first index finds the next element to remove (`writeIndex`), and the second one points to +subsequent element to keep (`readIndex`). Then we swap the elements and proceed +to the end. Then we can remove all the elements at the elements from +`writeIndex` to the end. ```swift /// Remove all selected shapes. From 04c4b5dad2eb8f0ae7c485163cc3ed33065b4cb1 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Tue, 3 Feb 2026 10:04:28 -0800 Subject: [PATCH 06/11] Update settings.json --- .vscode/settings.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index aa85728..6df185d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,10 +2,14 @@ "rewrap.autoWrap.enabled": true, "rewrap.wrappingColumn": 80, "cSpell.words": [ + "deinit", + "deinitialization", + "discardability", "footgun", "Gitter", "inout", "irreflexivity", + "postconditions", "preorder", "subranges", "unencapsulated" From 998d46c3dbead16387a0df44547afe426509f50f Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Tue, 3 Feb 2026 17:03:47 -0800 Subject: [PATCH 07/11] Revise algorithms chapter; add halfStablePartition Rewrite and clarify chapter-4-algorithms.md: improve prose, reorganize examples, and expand the discussion of loop invariants, termination and postconditions. Add a generic Swift halfStablePartition implementation and update removeAllSelected to use it; include usage example and additional notes on rotations, slide/gather composition, and algorithmic tradeoffs. Minor formatting and comment cleanup throughout the file and append extra efficiency bullets. Also add "Subrange" to the VS Code spelling/settings list in .vscode/settings.json. --- .vscode/settings.json | 1 + better-code/src/chapter-4-algorithms.md | 274 ++++++++++++++++++------ 2 files changed, 212 insertions(+), 63 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6df185d..6db659e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,7 @@ "irreflexivity", "postconditions", "preorder", + "Subrange", "subranges", "unencapsulated" ], diff --git a/better-code/src/chapter-4-algorithms.md b/better-code/src/chapter-4-algorithms.md index d0b105a..17ec259 100644 --- a/better-code/src/chapter-4-algorithms.md +++ b/better-code/src/chapter-4-algorithms.md @@ -2,11 +2,11 @@ >_
No raw loops.
_ -The most important part of software development is computing _something_. -Algorithms are an abstraction of computation, and every program is an algorithm. -It is easy to be distracted by class hierarchies, software architecture, design -patterns, etc. Such things are helpful only insofar as they aid in -implementing a correct and efficient algorithm. +Programming is about _computing_ something. Algorithms are an abstraction of +computation, and every program is an algorithm. It is easy to be distracted by +class hierarchies, software architecture, design patterns, etc. Such things are +helpful only insofar as they aid in implementing a correct and efficient +algorithm. **Algorithm** (n.): _a process or set of rules to be followed in calculations or other problem-solving operations, especially by a computer_ (New Oxford American @@ -21,9 +21,9 @@ For each component of the design, we will have a statement of what the component is or does. Components that _do_ something are operations, and the statement of what they do form the basis for the operations contract. -Consider a simple example. Suppose we are building a drawing tool and want -to remove a selected shape from an array. The naive implementation is a loop -that scans for the shape and erases it. +Consider a simple example. Suppose we are building a drawing tool and want to +remove a selected shape from an array. The naive implementation is a loop that +scans for the shape and erases it. ```swift /// Removes the selected shape. @@ -37,12 +37,14 @@ func removeSelected(shapes: inout [Shape]) { } ``` -Extend the requirement: the user can now select multiple shapes. The loop +We can extend the requirement to allow the user to select multiple shapes and +modify the code to remove the selected shapes. The loop becomes more complicated. Removing one element shifts the indices of the -remaining ones, so the code must compensate. +remaining ones, so the code must handle an additional case. + ```swift -/// Remove all selected shapes. (BAD) +/// Remove all selected shapes. func removeAllSelected(shapes: inout [Shape]) { var i = 0 while i < shapes.count { @@ -57,10 +59,11 @@ func removeAllSelected(shapes: inout [Shape]) { ``` We could simplify the loop by reversing the direction of iteration, since only -subsequent indices are affected by the removal. +subsequent indices are affected by the removal this removes the fix-up. + ```swift -/// Remove all selected shapes. (BAD) +/// Remove all selected shapes. func removeAllSelected(shapes: inout [Shape]) { for i in (0.. ## From Mechanism to Intent - -There is an algorithm known as half-stable partition which can be used to remove -the elements in linear time. The trick is to walk two indices forward so the -first index finds the next element to remove (`writeIndex`), and the second one points to -subsequent element to keep (`readIndex`). Then we swap the elements and proceed -to the end. Then we can remove all the elements at the elements from -`writeIndex` to the end. +We can remove the selected shapes more efficiently. The trick is to walk two +indices (`writeIndex` and `readIndex`) forward. The `writeIndex` finds the next +selected item to be removed and the `readIndex` finds the subsequent unselected +item. The shapes at the indices are swapped and we proceed until `readIndex` +reaches the end. Finally, we trim the array from `writeIndex` to the end to remove +all the selected elements. ```swift /// Remove all selected shapes. @@ -113,20 +113,97 @@ func removeAllSelected(shapes: inout [Shape]) { } ``` - +Take a moment to prove to yourself that the loop is correct. + +The act of verifying a loop requires us to establish: + +- A _loop invariant_ that holds before each iteration (including the first), and + at loop termination. +- A _variant function_ that maps to a strictly decreasing value on each to prove + termination +- A _postcondition_ that is a statement of the state of the loop invariant + at loop termination. + +In our above we would have the following: +- Loop invariant: + - All unselected elements before `readIndex` have been moved to the front. + - The region before `writeIndex` preserves the original relative order of + unselected elements. + - All elements in the range `writeIndex.. + + +As an exercise, look back at the two prior implementations of +`removeAllSelected()` and prove to yourself that the loop is correct. + +As with contracts, the processes of proving to ourselves that loops are +correct is something we do informally (hopefully) every time we write a loop. +Our code reviewers should also verify the loop is correct. +Even in this simple example it is easy to make a small mistake that could have +serious consequences. + +The best way to avoid complexity of loops is to learn to identify and compose +algorithms. The loop we just implemented is a permutation operation that +partitions our shapes into unselected and selected subsequences. The relative +order of the shapes in the unselected sequence is unchanged. This property is +known as "stability" so this operation is a half-stable partition. The algorithm +is not specific to shapes so we can lift it out into a generic algorithm. -Once we encapsulate the loops, the operations become simple. +```swift +/// Reorders the elements of the collection such that all the elements that match +/// the given predicate are after all the elements that don’t match, preserving +/// the relative order of the unmatched elements. +/// +/// - Returns: The index of the first element that satisfies the predicate. +func halfStablePartition( + _ array: inout [T], + by belongsInSecondPartition: (T) -> Bool +) -> Int { + var writeIndex = 0 + + // Partition: move all elements that don't satisfy predicate to the front + for readIndex in 0..No raw loops. -Software exists to compute. Everything else—types, classes, modules, architecture—is scaffolding around that central act. An algorithm is the abstraction of that act: a precise, repeatable composition of operations that transforms input into output. Every program is an algorithm, and every algorithm is a composition. +Software exists to compute. Everything else—types, classes, modules, +architecture—is scaffolding around that central act. An algorithm is the +abstraction of that act: a precise, repeatable composition of operations that +transforms input into output. Every program is an algorithm, and every algorithm +is a composition. -Raw loops hide this. A loop is a mechanism, not an idea. It tells the computer *how* to step, but it does not tell the reader *what* is being computed. This chapter is about replacing mechanisms with ideas—about expressing computation in terms of named, reusable algorithms rather than ad‑hoc control flow. +Raw loops hide this. A loop is a mechanism, not an idea. It tells the computer +*how* to step, but it does not tell the reader *what* is being computed. This +chapter is about replacing mechanisms with ideas—about expressing computation in +terms of named, reusable algorithms rather than ad‑hoc control flow. ## From Mechanism to Intent -Consider a simple example. Suppose we are building a small drawing tool and want to remove a selected shape from an array. The naive implementation is a loop that scans for the shape and erases it. It works, but the loop obscures the intent: “remove this element.” +Consider a simple example. Suppose we are building a small drawing tool and want +to remove a selected shape from an array. The naive implementation is a loop +that scans for the shape and erases it. It works, but the loop obscures the +intent: “remove this element.” -Extend the requirement: the user can now select multiple shapes. The naive loop becomes more complicated. Removing one element shifts the indices of the remaining ones, so the loop must compensate. Reversing the iteration order avoids the index drift, but the algorithm is still quadratic. The code is longer, more fragile, and no clearer about what it is trying to do. +Extend the requirement: the user can now select multiple shapes. The naive loop +becomes more complicated. Removing one element shifts the indices of the +remaining ones, so the loop must compensate. Reversing the iteration order +avoids the index drift, but the algorithm is still quadratic. The code is +longer, more fragile, and no clearer about what it is trying to do. -This is the cost of working at the level of mechanisms. The problem is not the example; the problem is the approach. +This is the cost of working at the level of mechanisms. The problem is not the +example; the problem is the approach. ## Algorithms Capture Patterns -A better approach is to express the operation directly: “keep the shapes that are not selected.” This is a partitioning problem. A half‑stable partition moves the elements we want to keep to the front and the ones we want to discard to the back. After that, removing the tail is trivial. +A better approach is to express the operation directly: “keep the shapes that +are not selected.” This is a partitioning problem. A half‑stable partition moves +the elements we want to keep to the front and the ones we want to discard to the +back. After that, removing the tail is trivial. -This is exactly what `delete_if` does. It is a named, reusable algorithm that captures a common pattern of computation. It replaces a fragile loop with a clear statement of intent. +This is exactly what `delete_if` does. It is a named, reusable algorithm that +captures a common pattern of computation. It replaces a fragile loop with a +clear statement of intent. -The important point is not the specific algorithm. It is the shift in thinking: from manipulating indices to expressing a transformation. +The important point is not the specific algorithm. It is the shift in thinking: +from manipulating indices to expressing a transformation. ## Discovering Algorithms Through Composition -As programs grow, we encounter new operations that are awkward to express with raw loops. Suppose we want to move a selected element to a new position. One way is to copy it out, erase it, and insert it back. This works, but it decomposes the operation into low‑level steps. The code describes the mechanics, not the intent. +As programs grow, we encounter new operations that are awkward to express with +raw loops. Suppose we want to move a selected element to a new position. One way +is to copy it out, erase it, and insert it back. This works, but it decomposes +the operation into low‑level steps. The code describes the mechanics, not the +intent. -A better expression of the intent is rotation. A rotation shifts a block of elements while preserving their order. Moving one element is a rotation of a block of size one. Moving several consecutive elements is the same pattern. The algorithm is the idea: “rotate this range so that these elements end up here.” +A better expression of the intent is rotation. A rotation shifts a block of +elements while preserving their order. Moving one element is a rotation of a +block of size one. Moving several consecutive elements is the same pattern. The +algorithm is the idea: “rotate this range so that these elements end up here.” -Rotation is a more powerful building block than a hand‑rolled loop. It composes cleanly with other algorithms, and it generalizes. +Rotation is a more powerful building block than a hand‑rolled loop. It composes +cleanly with other algorithms, and it generalizes. ## Generalizing Patterns: `slide` and `gather` -Once we recognize rotation as the underlying pattern, we can name the operation we actually want: sliding a range of elements to a new position. A `slide` algorithm expresses this directly. It takes a range and a target position and performs the appropriate rotation. The code is short, the intent is clear, and the algorithm is reusable. +Once we recognize rotation as the underlying pattern, we can name the operation +we actually want: sliding a range of elements to a new position. A `slide` +algorithm expresses this directly. It takes a range and a target position and +performs the appropriate rotation. The code is short, the intent is clear, and +the algorithm is reusable. -Sometimes the elements we want to move are not consecutive. We can still express the operation in terms of existing algorithms. A stable partition can collect (“gather”) the selected elements into a contiguous block. Once gathered, a single slide moves them as a unit. This yields a `gather` algorithm: a composition of two stable partitions and a slide. +Sometimes the elements we want to move are not consecutive. We can still express +the operation in terms of existing algorithms. A stable partition can collect +(“gather”) the selected elements into a contiguous block. Once gathered, a +single slide moves them as a unit. This yields a `gather` algorithm: a +composition of two stable partitions and a slide. -None of these algorithms depend on shapes. They depend only on iterators and predicates. They are generic. They capture patterns of computation that appear in many domains. +None of these algorithms depend on shapes. They depend only on iterators and +predicates. They are generic. They capture patterns of computation that appear +in many domains. -This is the power of algorithmic thinking: we discover general solutions by abstracting from specific problems. +This is the power of algorithmic thinking: we discover general solutions by +abstracting from specific problems. ## Algorithmic Tradeoffs and Guarantees -For many problems, there is more than one algorithm. Each has different tradeoffs. Some algorithms are greedy; some are lazy. Some operate in place; others require additional storage. Some require only forward iteration; others rely on random‑access. +For many problems, there is more than one algorithm. Each has different +tradeoffs. Some algorithms are greedy; some are lazy. Some operate in place; +others require additional storage. Some require only forward iteration; others +rely on random‑access. -Rotation is a good example. With forward iterators, rotation requires repeated swaps and is linear in the size of the range. With random‑access iterators, we can rotate more efficiently by reversing subranges. The stronger the guarantees on the types, the more efficient the algorithm can be. +Rotation is a good example. With forward iterators, rotation requires repeated +swaps and is linear in the size of the range. With random‑access iterators, we +can rotate more efficiently by reversing subranges. The stronger the guarantees +on the types, the more efficient the algorithm can be. -Choosing the right algorithm requires understanding these tradeoffs. This is part of the craft of programming: matching the problem to the algorithmic vocabulary. +Choosing the right algorithm requires understanding these tradeoffs. This is +part of the craft of programming: matching the problem to the algorithmic +vocabulary. ## Algorithms Form a Vocabulary -Algorithms are not limited to rearranging elements in a container. Numeric algorithms, graph algorithms, geometric algorithms, and string algorithms are all compositions of operations. Even `swap` is an algorithm: a minimal, correct, reusable composition. +Algorithms are not limited to rearranging elements in a container. Numeric +algorithms, graph algorithms, geometric algorithms, and string algorithms are +all compositions of operations. Even `swap` is an algorithm: a minimal, correct, +reusable composition. -As our vocabulary grows, we can express more ideas directly. We write less code, and the code we write is clearer. We stop thinking in terms of loops and start thinking in terms of transformations. +As our vocabulary grows, we can express more ideas directly. We write less code, +and the code we write is clearer. We stop thinking in terms of loops and start +thinking in terms of transformations. ## Structure as Algorithmic Leverage -Sometimes the best way to improve an algorithm is to change the shape of the data. Consider searching for a value in a range. A linear search is simple but takes time proportional to the size of the range. If we sort the data, we impose a structure: if `i < j`, then `a[i] <= a[j]`. This structure enables binary search, which finds a value in logarithmic time. +Sometimes the best way to improve an algorithm is to change the shape of the +data. Consider searching for a value in a range. A linear search is simple but +takes time proportional to the size of the range. If we sort the data, we impose +a structure: if `i < j`, then `a[i] <= a[j]`. This structure enables binary +search, which finds a value in logarithmic time. -The algorithm did not change; the data did. By structuring the data, we made a more efficient algorithm possible. +The algorithm did not change; the data did. By structuring the data, we made a +more efficient algorithm possible. -This is a general principle: data structure is algorithmic leverage. The representation of the data determines which algorithms are available and how efficient they can be. +This is a general principle: data structure is algorithmic leverage. The +representation of the data determines which algorithms are available and how +efficient they can be. ## Bridge to the Next Chapter -Sorting is the simplest example of structuring data to enable efficient algorithms. More sophisticated structures—trees, heaps, hash tables—provide even more leverage. They make certain operations fast by organizing data in ways that algorithms can exploit. +Sorting is the simplest example of structuring data to enable efficient +algorithms. More sophisticated structures—trees, heaps, hash tables—provide even +more leverage. They make certain operations fast by organizing data in ways that +algorithms can exploit. -The next chapter explores these structures. Here, we have focused on the algorithms themselves: named, reusable compositions that express intent, improve clarity, and enable efficient computation. +The next chapter explores these structures. Here, we have focused on the +algorithms themselves: named, reusable compositions that express intent, improve +clarity, and enable efficient computation. ### @@ -291,3 +434,8 @@ assist you in finding an algorithm to solve a problem. - Lazy ## Efficiency + +- time +- space (memory) +- energy +- computational (number of ALUs in use) From ebd295b741eb4bcbfe2c7f721bdb865fc067509c Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Tue, 3 Feb 2026 17:07:45 -0800 Subject: [PATCH 08/11] Remove redundant comment and usage example in algorithms doc Clean up chapter-4-algorithms.md by removing an inline partition comment and a short usage example for halfStablePartition. This trims redundant explanatory lines while leaving the function implementation unchanged. --- better-code/src/chapter-4-algorithms.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/better-code/src/chapter-4-algorithms.md b/better-code/src/chapter-4-algorithms.md index 17ec259..b41f353 100644 --- a/better-code/src/chapter-4-algorithms.md +++ b/better-code/src/chapter-4-algorithms.md @@ -169,7 +169,6 @@ func halfStablePartition( ) -> Int { var writeIndex = 0 - // Partition: move all elements that don't satisfy predicate to the front for readIndex in 0..( return writeIndex } - -// Usage example: -// let pivot = halfStablePartition(&shapes, by: { $0.isSelected }) -// shapes.removeSubrange(pivot...) // Remove all selected shapes ``` Given `halfStablePartition()` we can rewrite `removeAllSelected()`. From e3d6f9d0c6b229cf9e42ee10e3d5cc0eb9396ffb Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Thu, 19 Feb 2026 16:54:03 -0800 Subject: [PATCH 09/11] Algorithms update --- better-code/src/Appendix - Safety.md | 82 ++++++++++++ better-code/src/chapter-4-algorithms.md | 164 +++++++++++++++++------- 2 files changed, 198 insertions(+), 48 deletions(-) create mode 100644 better-code/src/Appendix - Safety.md diff --git a/better-code/src/Appendix - Safety.md b/better-code/src/Appendix - Safety.md new file mode 100644 index 0000000..3305b81 --- /dev/null +++ b/better-code/src/Appendix - Safety.md @@ -0,0 +1,82 @@ +## Terminology + +- **Safety** in engineering is the prevention of harm through system design. + +- An **operation** is any executable program or program fragment, from an integer addition to a whole application. + + +- A **[safety property](https://en.wikipedia.org/wiki/Safety_and_liveness_properties)** is the *impossibility* of some occurrence when an operation is used correctly. For example, this function upholds the safety property that nothing is printed to the console: + + ```swift + /// Returns `x`. + /// + /// - Precondition: `x >= 0` + func identity(_ x: Int) -> Int { + if x < 0 { print("Precondition violated!") } + return x + } + ``` +To be a safety property, it must *compose*. That is, when any two operations *P* and *Q* uphold the property, so does *P* followed by *Q*. For example, freedom from data races is a safety property, but freedom from logical races is not, because when two consecutive non-racy mutations are composed into a larger mutation, another thread can observe the partially mutated state between the two steps. + +- A **[liveness property](https://en.wikipedia.org/wiki/Safety_and_liveness_properties)** is the *guarantee* of some occurrence when an operation is used correctly. For example, this function upholds the liveness property that it eventually returns: + + ```swift + /// Returns `x`. + /// + /// - Precondition: `x >= 0` + func identity2(_ x: Int) -> Int { + while x < 0 { /* loop forever */ } + return x + } + ``` + +- An ***X* safe operation** upholds some safety property *X* **even if preconditions are violated**. [^qualification] For example, when `a` is an array, `a[0] = 3` never modifies a variable not mentioned in the expression, even if `a` is empty (which violates the precondition of `a[0]`). We might say that the operation is “expression-mutation safe.” + +[^qualification]: note this important distinction—an operation can uphold the memory safety property but not be memory-safe by this definition, because the former depends on preconditions being satisfied but the latter does not. + +- An ***X* safe language** is one where all primitive operations are *X safe*. It follows that all non-primitive operations—and all possible programs in the language—are *X safe*. A language subset, such as “Swift programs in which no identifier contains the substring `Unsafe` or `unsafe`,” can be considered a language. + +- **Memory safety**: the property that invalid memory operations such as out-of-bounds accesses and use-after-free do not occur. + +- **Type safety**: the property that an instance of one type is never accessed as an instance of another type. + +- **Thread safety**: the property that a data race does not occur. Sometimes “thread safe” is used to mean that, additionally, deadlock does not occur. Freedom from deadlock can also be viewed as part of a liveness property guaranteeing forward progress. + +- **Data race safety**: the property that a data race does not occur (explicitly excluding freedom from deadlock as a constraint). + +- **Undefined behavior** is not bounded by any constraints and thus nullifies every safety property. An operation that can have undefined behavior, or a language that includes such an operation, is never *X* safe for any *X*. + + Violations of memory safety, type safety, and data race safety have effects that can't be usefully described in terms of any portable programming language. For example, the effects of an out-of-bounds write can be understood when memory is viewed as a linear collection of bytes, but can't be described in terms of distinct variables and constants of many types. Therefore, in unsafe programming languages, these violations typically cause undefined behavior.[^java-data-race] + +- A **safe operation** will never exhibit undefined behavior, even if preconditions are violated. Safety is often a consequence of type checking (you can't access `x.5` when `x` is a 2-element tuple), but sometimes runtime checks are needed, as when indexing a variable-length array. “Trapping” or otherwise stopping the program when preconditions are violated is one way to achieve safety. + +- A **safe language** (such as Java or Haskell) has only safe operations, so all possible programs in the language are safe. The distinction is important because proving a safety property of arbitrary code is tedious and sometimes very difficult, unless—as with a safe language—all code is safe by construction. + +- In practice, “**memory-safe language**” is synonymous with “safe language.” Since undefined behavior invalidates all guarantees (including memory safety), a memory-safe language can have no undefined behavior and is therefore a safe language. Because the behavior of a memory safety violation can't be defined at the language level, any language without undefined behavior must be memory safe. + +- A **safe-by-default language** (such as Swift or Rust) contains a minority of unsafe operations that can be easily recognized by tooling and banned or flagged for extra scrutiny in code review. This arrangement provides unconditional safety in most code while allowing the direct use of primitive operations such as pointer dereferencing, without expensive validity checks. When unsafe operations are used correctly in the implementation details of safe abstractions, the vocabulary of safe operations grow, with little compromise to overall security. Safe-by-default languages are often referred to as “memory safe” despite the availability of operations that can compromise memory safety. + +- The **safe subset of a safe-by-default language** is a safe language. + +[^java-data-race]: Some languages, such as Java and JavaScript, define the behavior of data races, but in such a way as to be useless for most programming. + + +---- + +In Lamport’s framework, safety is defined semantically—as a prefix‑closed set of behaviors—but this definition alone does not guarantee compositionality under functional composition. As Abadi and Lamport show in Composing Specifications, and as later clarified by Abadi and Plotkin’s work on refinement‑preserving transformations, safety properties become compositional only when the functions involved are themselves safety‑preserving. In other words, from the fact that a safety property p holds for f(x) and for g(x), nothing follows about p(f(g(x))) unless f and g each preserve p. This distinction—emphasized in surveys such as Freiling and Santen’s work on compositional reasoning—makes clear that prefix‑closure characterizes the semantic nature of safety, while congruence under composition requires an additional structural assumption about the operators acting on behaviors. + +preserving.dvi +https://lamport.org/pubs/abadi-preserving.pdf +lamport.org + + +99583.99626 +https://dlnext.acm.org/doi/pdf/10.1145/99583.99626 +dlnext.acm.org + + +On the Composition of Compositional Reasoning | Springer Nature Link +https://link.springer.com/chapter/10.1007/11786160_8 +link.springer.com + +https://lamport.azurewebsites.net/pubs/abadi-composing.pdf diff --git a/better-code/src/chapter-4-algorithms.md b/better-code/src/chapter-4-algorithms.md index b41f353..9540a1d 100644 --- a/better-code/src/chapter-4-algorithms.md +++ b/better-code/src/chapter-4-algorithms.md @@ -86,30 +86,53 @@ something, another to remove it, another to insert it somewhere else, and perhaps another to repair the structure afterward --> -## From Mechanism to Intent -We can remove the selected shapes more efficiently. The trick is to walk two -indices (`writeIndex` and `readIndex`) forward. The `writeIndex` finds the next -selected item to be removed and the `readIndex` finds the subsequent unselected -item. The shapes at the indices are swapped and we proceed until `readIndex` -reaches the end. Finally, we trim the array from `writeIndex` to the end to remove -all the selected elements. +## Intent to Mechanism + +We can remove the selected shapes more efficiently. The trick is to collect all +of the unselected shapes at the start of the array, and then remove the +remaining shapes all at once. We don't care about the shapes to be removed or +their order. + +To design an algorithm to collect the unselected shapes, we use a common +technique of stating the postcondition and then seeing if there is a way to +extend the postcondition to additional elements. + +The desired postcondition for collecting the unselected shapes is that all the +unselected shapes are in the range `0... The other option is to swap the element at +`shapes[r]` with the element at `shapes[p]`. + +[^two-ways]: Depending on your language, there may be additional options. We + could relocate (move) the shapes. In Swift, we could do this with unsafe + operations, but we would leave uninitialized memory at the end of the array, + and there is no operation on the standard array to trim it. + +Both approaches will preserve the relative +order of the unselected shapes. Now we can write the code: ```swift /// Remove all selected shapes. func removeAllSelected(shapes: inout [Shape]) { - var writeIndex = 0 + var p = 0 - for readIndex in 0.. + As an exercise, look back at the two prior implementations of -`removeAllSelected()` and prove to yourself that the loop is correct. +`removeAllSelected()` and prove to yourself that the loops are correct. As with contracts, the processes of proving to ourselves that loops are correct is something we do informally (hopefully) every time we write a loop. @@ -154,31 +179,29 @@ The best way to avoid complexity of loops is to learn to identify and compose algorithms. The loop we just implemented is a permutation operation that partitions our shapes into unselected and selected subsequences. The relative order of the shapes in the unselected sequence is unchanged. This property is -known as "stability" so this operation is a half-stable partition. The algorithm +known as _stability_, so this operation is a half-stable partition. The algorithm is not specific to shapes so we can lift it out into a generic algorithm. ```swift -/// Reorders the elements of the collection such that all the elements that match -/// the given predicate are after all the elements that don’t match, preserving -/// the relative order of the unmatched elements. -/// -/// - Returns: The index of the first element that satisfies the predicate. -func halfStablePartition( - _ array: inout [T], - by belongsInSecondPartition: (T) -> Bool -) -> Int { - var writeIndex = 0 - - for readIndex in 0.. Bool + ) -> Index { + var p = startIndex + + for r in indices { + if !belongsInSecondPartition(self[r]) { + swapAt(p, r) + formIndex(after: &p) } - writeIndex += 1 } + + return p } - - return writeIndex } ``` @@ -186,10 +209,55 @@ Given `halfStablePartition()` we can rewrite `removeAllSelected()`. ```swift func removeAllSelected(shapes: inout [Shape]) { - shapes.removeSubrange(halfStablePartition(&shapes, by: { $0.isSelected })...) + shapes.removeSubrange(shapes.halfStablePartition(by: { $0.isSelected })...) } ``` +Although we can't do better than linear time, our implementation of +`halfStablePartition()` is doing unnecessary work by calling swap when `p == r`. +As an exercise, before entering the loop, find the first point where a swap will +be required, prove the new implementation is correct. Create a benchmark to +compare the performance of the two implementations. + + + +Often, we view _efficiency_ as the upper-bound (big-_O_) of how an algorithm +scales in the worst case. Scaling is important, but so are metrics like how much +wall clock time the operation takes or how much memory the operation consumes. +In the software industry if a competitors approach takes half the time or runs +in production at half the energy costs, that could be a significant advantage. + +We define _efficiency of an operation_ as the minimization of resource the +operation uses to calculate a result. The resources include: + +- time +- memory +- energy +- computational hardware + +Because memory access is slow, energy consumption is largely determined by the +amount of time an operation takes, and computational hardware is often +underutilized, we prioritize optimizing time. But as we will see in the +Concurrency chapter, balancing all of these resources is critical for an +efficient system design. + +When designing algorithms it is important to have a rough sense of the cost of +primitive operations to guide the design. Some approximate numbers by order of +magnitude[^operation-costs]: + +| Cycles | Operations | +|---|---| +| 10^0 | basic register operation (add, mul, or), memory write, predicted branch, L1 read | +| 10^1 | L2 and L3 cache read, branch misprediction, division, atomic operations, function call | +| 10^2 | main memory read | +| 10^3 | Kernel call, thread context switch (direct costs), exception thrown and caught | +| 10^4 | Thread context switch (including cache invalidation) | + +[^operation costs]: [_Infographics: Operation Costs in CPU Clock + Cycles_](http://ithare.com/infographics-operation-costs-in-cpu-clock-cycles/) + + + A rotation expresses “move this range here.” A stable partition expresses “collect the elements that satisfy this predicate.” A slide is a composition of rotations. A gather is a composition of stable partitions. From 089ba6d09f09e9d2c6777614fe6e904e3c9e26ea Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Fri, 20 Feb 2026 09:57:10 -0800 Subject: [PATCH 10/11] Fix footnote label in chapter-4-algorithms.md Rename footnote identifier from [^operation costs] to [^operation-costs] to match the reference and ensure the footnote link resolves correctly in the algorithms chapter. --- better-code/src/chapter-4-algorithms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/better-code/src/chapter-4-algorithms.md b/better-code/src/chapter-4-algorithms.md index 9540a1d..bc66a2c 100644 --- a/better-code/src/chapter-4-algorithms.md +++ b/better-code/src/chapter-4-algorithms.md @@ -253,7 +253,7 @@ magnitude[^operation-costs]: | 10^3 | Kernel call, thread context switch (direct costs), exception thrown and caught | | 10^4 | Thread context switch (including cache invalidation) | -[^operation costs]: [_Infographics: Operation Costs in CPU Clock +[^operation-costs]: [_Infographics: Operation Costs in CPU Clock Cycles_](http://ithare.com/infographics-operation-costs-in-cpu-clock-cycles/) From cd587146803207f7b14c7ea1d47ff8d45dffd913 Mon Sep 17 00:00:00 2001 From: Sean Parent Date: Fri, 20 Feb 2026 10:04:37 -0800 Subject: [PATCH 11/11] Add note about ordering contracts; clean example comment Insert an HTML TODO-style note calling out the need to discuss strict-weak vs total-preorder from the contracts chapter, and remove an inline "// lower bound?" comment from the Swift example to keep the code example clean. Changes are limited to chapter-4-algorithms.md to flag the conceptual discussion and tidy the sample code. --- better-code/src/chapter-4-algorithms.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/better-code/src/chapter-4-algorithms.md b/better-code/src/chapter-4-algorithms.md index bc66a2c..42e46e3 100644 --- a/better-code/src/chapter-4-algorithms.md +++ b/better-code/src/chapter-4-algorithms.md @@ -462,6 +462,8 @@ clarity, and enable efficient computation. {{#include test.swift:2:5}} ``` + + ```swift func partitionPoint(_ array: [T], _ predicate: (T) -> Bool) -> Int { var left = 0 @@ -478,7 +480,7 @@ func partitionPoint(_ array: [T], _ predicate: (T) -> Bool) -> Int { } let array = [1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 10] -let index = partitionPoint(array, { $0 <= 5 }) // lower bound? +let index = partitionPoint(array, { $0 <= 5 }) ``` ### Algorithm categories