From 9d653a5814f8990f9084cb9232ab79fc7bec74f1 Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Thu, 25 Jun 2026 15:21:00 +1000 Subject: [PATCH] words from community solutions We now teach - >hashtable - ?nth - adjoin-all - map-find - while* --- .../.docs/introduction.md | 9 ++++++ .../.docs/introduction.md | 24 ++++++++++++++ .../lighthouse-logbook/.docs/introduction.md | 18 +++++++++-- .../mixed-juices/.docs/introduction.md | 31 +++++++++++++++++++ .../pursers-pantry/.docs/introduction.md | 19 ++++++++++++ 5 files changed, 98 insertions(+), 3 deletions(-) diff --git a/exercises/concept/backyard-birdwatcher/.docs/introduction.md b/exercises/concept/backyard-birdwatcher/.docs/introduction.md index df3ca54..861393d 100644 --- a/exercises/concept/backyard-birdwatcher/.docs/introduction.md +++ b/exercises/concept/backyard-birdwatcher/.docs/introduction.md @@ -26,6 +26,7 @@ rest ( seq -- tailseq ) ! everything but the first element last ( seq -- elt ) but-last ( seq -- headseq ) ! everything but the last element nth ( n seq -- elt ) ! 0-based +?nth ( n seq -- elt/f ) ! 0-based; f when out of bounds ``` ```factor @@ -37,6 +38,14 @@ nth ( n seq -- elt ) ! 0-based 1 { 10 20 30 } nth . ! => 20 ``` +`?nth` is the bounds-safe version: you can probe a position without +checking the length first. + +```factor +1 { 10 20 30 } ?nth . ! => 20 +9 { 10 20 30 } ?nth . ! => f (out of bounds, no error) +``` + ## Slicing ``` diff --git a/exercises/concept/boutique-bookkeeping/.docs/introduction.md b/exercises/concept/boutique-bookkeeping/.docs/introduction.md index f4b4a45..b1968ea 100644 --- a/exercises/concept/boutique-bookkeeping/.docs/introduction.md +++ b/exercises/concept/boutique-bookkeeping/.docs/introduction.md @@ -47,6 +47,30 @@ sift ( seq -- newseq ) ! drop every `f`, keep the rest element except `f`, handy for clearing out the gaps a previous `map` left behind. +### Keeping the result of the test + +`find` hands back the matching *element*. When the test already +*computes* the value you actually want, `map-find` saves you a +second pass: it runs the quotation over each element and returns the +first non-`f` result **and** the element that produced it. + +``` +map-find ( seq quot: ( elt -- result/f ) -- result elt ) +``` + +```factor +USING: kernel math sequences ; + +! First multiple of 3, paired with that multiple divided by 3. +{ 11 13 15 17 } [ dup 3 mod 0 = [ 3 /i ] [ drop f ] if ] map-find .s +! => 5 (result: 15/3) +! => 15 (element) +``` + +The quotation returns `f` for elements that don't qualify and the +result you want to keep for the one that does. If nothing qualifies, +`map-find` leaves `f f`, just like `find`. + ### Filtering records by a slot When the elements are *tuples*, the predicate usually projects diff --git a/exercises/concept/lighthouse-logbook/.docs/introduction.md b/exercises/concept/lighthouse-logbook/.docs/introduction.md index 82a63f0..d35fbf0 100644 --- a/exercises/concept/lighthouse-logbook/.docs/introduction.md +++ b/exercises/concept/lighthouse-logbook/.docs/introduction.md @@ -36,11 +36,12 @@ USING: hash-sets prettyprint ; ## Adjoining and removing ``` -adjoin ( elt set -- ) ! insert in place; no-op if already present -delete ( elt set -- ) ! remove in place; no-op if absent +adjoin ( elt set -- ) ! insert in place; no-op if already present +adjoin-all ( seq set -- ) ! insert every element of seq in place +delete ( elt set -- ) ! remove in place; no-op if absent ``` -Both mutate the set; neither returns anything on the stack. +All three mutate the set; none returns anything on the stack. ```factor USING: hash-sets kernel sets ; @@ -52,6 +53,17 @@ HS{ } clone . ! => HS{ "NS-1024" "WB-203" } ``` +`adjoin-all` is the bulk form: it adjoins each element of a +sequence, skipping duplicates just like `adjoin` does one at a time. + +```factor +USING: hash-sets kernel sets ; + +HS{ "NS-1024" } clone +{ "WB-203" "NS-1024" "QR-7" } over adjoin-all +. ! => HS{ "QR-7" "NS-1024" "WB-203" } (order not guaranteed) +``` + ## Asking the set ``` diff --git a/exercises/concept/mixed-juices/.docs/introduction.md b/exercises/concept/mixed-juices/.docs/introduction.md index 8433f48..2072b19 100644 --- a/exercises/concept/mixed-juices/.docs/introduction.md +++ b/exercises/concept/mixed-juices/.docs/introduction.md @@ -33,6 +33,37 @@ body `dup . 1 -` prints it and decrements it; the loop stops once the counter reaches `0`. The trailing `drop` discards that final `0` from the stack. +## `while*` — keep the predicate's value + +``` +while* ( pred body -- ) +``` + +`while` only cares whether its predicate left a true value — it +throws the value itself away before running the body. `while*` +instead *hands that value to the body*. Reach for it whenever the +test and the body want the same freshly-computed thing: a lookup, +a match, a parsed token. + +Here each name points to the next one in a chain. The predicate +looks the current name up; as long as there is a next name, `while*` +passes it to the body, which prints it and carries on from there: + +```factor +USING: assocs kernel locals prettyprint ; + +:: print-chain ( start links -- ) + start [ links at ] [ dup . ] while* ; + +"a" H{ { "a" "b" } { "b" "c" } } print-chain +! prints "b" then "c" +``` + +`links at` returns the next name, or `f` once a name has no entry. +While it returns a name, `while*` gives that name to `dup .`, which +prints it and leaves it as the key for the next lookup. The first +`f` stops the loop — `while*` discards it, so nothing is left over. + ## Multiple state values with locals When the loop carries more than one value — a remaining list and diff --git a/exercises/concept/pursers-pantry/.docs/introduction.md b/exercises/concept/pursers-pantry/.docs/introduction.md index 42003a5..4d446e7 100644 --- a/exercises/concept/pursers-pantry/.docs/introduction.md +++ b/exercises/concept/pursers-pantry/.docs/introduction.md @@ -135,6 +135,25 @@ H{ { "wood" 11 } { "coal" 7 } } sort-keys . ! => { { "coal" 7 } { "wood" 11 } } ``` +## From pairs back to a hashtable + +`>hashtable` (in [`hashtables`][hashtables]) is the inverse of +`>alist`: it turns any assoc — most often an alist of +`{ key value }` pairs — into a hashtable with O(1) lookup. + +``` +>hashtable ( assoc -- hashtable ) +``` + +```factor +{ { "coal" 7 } { "wood" 11 } } >hashtable . +! => H{ { "wood" 11 } { "coal" 7 } } (entry order not guaranteed) +``` + +Handy when you've assembled or transformed a list of pairs and +want to fold it back into a hashtable to look entries up by key. + [assocs]: https://docs.factorcode.org/content/vocab-assocs.html [fry]: https://docs.factorcode.org/content/article-fry.html +[hashtables]: https://docs.factorcode.org/content/vocab-hashtables.html [sorting]: https://docs.factorcode.org/content/vocab-sorting.html