Part IV. Reference · this page as markdown

Answers to the exercises

One way to answer each exercise. Most exercises have many good answers; where an answer is code, it was run like every other example in the book, and what it gave is shown.

Forms and values #

  1. A week's hours:
#ans-forms-1
(* 24 7)
It answers
168
  1. Sums inside text:
#ans-forms-2
(str (+ 1 2) " bells, " (+ 1 1) " chimes")
It answers
"3 bells, 2 chimes"
  1. str always answers text, so (type (str 1 2)) is :text:
#ans-forms-3
(type (str 1 2))
It answers
:text
  1. A map for a bell:
#ans-forms-4
{:name "Bell" :rings 4}
It answers
{:name "Bell", :rings 4}

Names #

  1. A room's area:
#ans-names-1
(let [width 4
      depth 3]
  (* width depth))
It answers
12
  1. name is a word of the language, so don't take it apart into a name of that name: take :rings apart, and read :name with its keyword.
#ans-names-2
(let [bell {:name "Bell" :rings 7}
      {:keys [rings]} bell]
  (str (:name bell) ": " rings))
It answers
"Bell: 7"
  1. count is a word of the language. Even where Plazmi renames it for you and the plan runs, a reader sees count and thinks of the word. Call it stools, or whatever it counts.
  1. Adding up 0 to 9 with an atom:
#ans-names-4
(let [total (atom 0)]
  (dotimes [i 10]
    (swap! total + i))
  (deref total))
It answers
45

Text, lists and maps #

  1. Capitals, joined:
#ans-text-1
(join ", " (map upper ["bell" "chime" "gong"]))
It answers
"BELL, CHIME, GONG"
  1. Counting words:
#ans-text-2
(count (words "Mira says the tea is ready"))
It answers
6
  1. Two keys set at once (assoc takes as many pairs as you give it):
#ans-text-3
(assoc {:rings 0} :rings 1 :last "@mira")
It answers
{:rings 1, :last "@mira"}
  1. The last two, kept as a list:
#ans-text-4
(vec (take-last 2 (range 10)))
It answers
[8 9]
  1. Mentions, and starts with:
#ans-text-5
(includes? "rain later" "rain")
(starts-with? "rain later" "rain")
It answers
true
true

Tests and choices #

  1. Three ways, narrowest first:
#ans-choices-1
(let [temperature 15]
  (cond (< temperature 10) "cold"
        (< temperature 20) "mild"
        :else "warm"))
It answers
"mild"
  1. A greeting for each time of day:
#ans-choices-2
(case :dusk
  :dawn "Up early."
  :day "Good day."
  :dusk "Good evening."
  :night "Good night.")
It answers
"Good evening."
  1. or answers the first value that counts as true, or the last if none does; and answers the last if all do:
#ans-choices-3
(or nil false)
(and 1 2 3)
It answers
false
3
  1. The first even number, doubled:
#ans-choices-4
(if-let [n (first (filter even? [1 3 4 5]))]
  (* 2 n)
  "none")
It answers
8

Working over lists #

  1. Squares, kept:
#ans-lists-1
(mapv (fn [n] (* n n)) (range 1 11))
It answers
[1 4 9 16 25 36 49 64 81 100]
  1. Who came most: count each, then take the key with the biggest count:
#ans-lists-2
(key (apply max-key val (frequencies ["@mira" "@cairn" "@mira" "@wisp" "@mira"])))
It answers
"@mira"
  1. The longest word, by folding:
#ans-lists-3
(reduce (fn [longest w] (if (> (count w) (count longest)) w longest))
        ""
        ["oak" "willow" "fern"])
It answers
"willow"
  1. Names starting @m:
#ans-lists-4
(filter #(starts-with? % "@m") ["@mira" "@cairn" "@moss"])
It answers
("@mira" "@moss")

Functions #

  1. A function that shouts:
#ans-fn-1
(defn shout [t] (str (upper t) "!"))
(shout "ting")
It answers
"TING!"
  1. Plus 100:
#ans-fn-2
(map #(+ % 100) [1 2 3])
It answers
(101 102 103)
  1. Adding up a list by calling itself, and with reduce:
#ans-fn-3
(defn sum-list [l]
  (if (empty? l) 0 (+ (first l) (sum-list (rest l)))))
(sum-list [1 2 3 4])
(reduce + 0 [1 2 3 4])
It answers
10
10
  1. The first power of 2 over 1,000:
#ans-fn-4
(loop [n 1]
  (if (> n 1000) n (recur (* n 2))))
It answers
1024

Chance that repeats #

  1. Three dice; the total is chance, from 3 to 18:
#ans-chance-1
(+ (inc (rand-int 6)) (inc (rand-int 6)) (inc (rand-int 6)))
It answers
…
  1. A week, shuffled:
#ans-chance-2
(shuffle ["Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday"])
It answers
[…]
  1. Each run's seed is made from who runs it as well as the world's place in its history, so two residents at the same moment start from different seeds.
  1. A coin:
#ans-chance-4
(defobject coin
  {:name "Coin"
   :verbs {:flip (fn [self who] (say self (rand-nth ["Heads." "Tails."])))}})
Played
> flip coin
[Juniper's coin] …

Reading your own home #

  1. Spending 35 of 50:
#ans-home-1
(if (>= (coins) 35) (- (coins) 35) "not enough")
It answers
15
  1. The rooms, as one text:
#ans-home-2
(join ", " (map :id (:rooms (house))))
It answers
"kitchen, living, bath, bedroom"
  1. By the time of day:
#ans-home-3
(if (= :night (:time (sky))) "Bonsoir." "Bonjour.")
It answers
"Bonsoir."
  1. Hours until dusk, at 18:00, from the hour in "HH:MM". (In the book's world it is just after one in the morning.)
#ans-home-4
(let [hour (parse-long (first (split (:time (world-time)) ":")))]
  (- 18 hour))
It answers
17

A first toy #

  1. A kettle:
#ans-toy-1
(defobject kettle
  {:name "Kettle"
   :state {:cups 0}
   :verbs {:pour (fn [self who]
                   (put! self :cups (inc (:cups self)))
                   (say self (str "A cup poured. That's " (:cups self) " today.")))}})
Played
> pour kettle
[Juniper's kettle] A cup poured. That's 1 today.
  1. A bell that can be hushed:
#ans-toy-2
(defobject bell
  {:name "Bell"
   :state {:rings 0}
   :verbs {:ring (fn [self who]
                   (put! self :rings (inc (:rings self)))
                   (say self (str "Ting. " (:rings self) " so far.")))
           :hush (fn [self who]
                   (put! self :rings 0)
                   (say self "The bell is still. Back to none."))}})
Played
> ring bell
[Juniper's bell] Ting. 1 so far.
> hush bell
[Juniper's bell] The bell is still. Back to none.
> ring bell
[Juniper's bell] Ting. 1 so far.
  1. A first play, and every one after:
#ans-toy-3
(defobject music-box
  {:name "Music box"
   :state {:plays 0}
   :verbs {:wind (fn [self who]
                   (put! self :plays (inc (:plays self)))
                   (say self (if (= 1 (:plays self))
                               "A tune you've never heard before."
                               "The same tune, a little slower.")))}})
Played
> wind music box
[Juniper's music box] A tune you've never heard before.
> wind music box
[Juniper's music box] The same tune, a little slower.
  1. A verb that says nothing still runs; the visitor is told they played it, and the toy says nothing:
#ans-toy-4
(defobject pebble
  {:name "Pebble"
   :verbs {:turn (fn [self who] nil)}})
Played
> turn pebble

A toy that listens #

  1. An owl that hears "night" only as a whole word:
#ans-listen-1
(defobject owl
  {:name "Owl"
   :on-hear (fn [self who text]
              (when (some #{"night"} (words (lower text)))
                (say self "Hoo.")))})
Played
> say what a night
[Juniper's owl] Hoo.
> say a nightingale sang
  1. A riddle chosen by a count: keep :riddle 0 in the state, a list of riddles and answers, and read the one at (mod riddle (count riddles)); when it's answered, add one to :riddle.
  1. "Bless you." only for words said to nobody:
#ans-listen-3
(defobject tabby
  {:name "Tabby"
   :on-hear (fn [self who text]
              (when (and (nil? (:to self)) (not (:spoken-to self)) (includes? (lower text) "achoo"))
                (say self "Bless you.")))})
Played
> say achoo
[Juniper's tabby] Bless you.
  1. A mirror for its maker alone: test (= who "@you") as well as (:spoken-to self).

Keeping time #

  1. A calendar of dawns:
#ans-dawn-1
(defobject calendar
  {:name "Calendar"
   :state {:day 0}
   :verbs {:read (fn [self who] (say self (str "Day " (:day self))))}
   :on-tick (fn [self] (put! self :day (inc (:day self))))})
Played
> read calendar
[Juniper's calendar] Day 0
  1. By the time of day, in a toy:
#ans-dawn-2
(defobject door-plate
  {:name "Door plate"
   :verbs {:read (fn [self who]
                   (say self (if (= :night (:time (sky))) "Bonsoir." "Bonjour.")))}})
Played
> read door plate
[Juniper's door plate] Bonsoir.
  1. A weather vane:
#ans-dawn-3
(defobject vane
  {:name "Weather vane"
   :verbs {:spin (fn [self who] (say self (str "The wind is " (name (:wind (weather))) ".")))}})
Played
> spin weather vane
[Juniper's weather vane] The wind is ….
  1. A glow of mint:
#ans-dawn-4
(defobject mint-stone
  {:name "Mint stone"
   :verbs {:rub (fn [self who]
                  (if (glow-colour "mint")
                    (say self "A cool green glow.")
                    (say self "Nothing happens this hour.")))}})
Played
> rub mint stone
[Juniper's mint stone] A cool green glow.

A toy with a memory #

  1. Ten names, never the same twice running:
#ans-memory-1
(defobject guest-log
  {:name "Guest log"
   :state {:guests []}
   :verbs {:sign (fn [self who]
                   (let [guests (or (:guests self) [])]
                     (when (not= who (last guests))
                       (put! self :guests (vec (take-last 10 (conj guests who)))))
                     (say self (str "Signed by " (join ", " (:guests self)) "."))))}})
Played
> sign guest log
[Juniper's guest log] Signed by Mira.
> sign guest log
[Juniper's guest log] Signed by Mira.
  1. Votes for each visitor, and who voted most:
#ans-memory-2
(defobject ballot-box
  {:name "Ballot box"
   :state {:votes {}}
   :verbs {:vote (fn [self who]
                   (put! self :votes (update (:votes self) who (fnil inc 0)))
                   (say self "Your vote is in."))
           :results (fn [self who]
                      (if (empty? (:votes self))
                        (say self "No votes yet.")
                        (let [[top n] (apply max-key val (:votes self))]
                          (say self (str top " has voted most: " n ".")))))}})
Played
> vote ballot box
[Juniper's ballot box] Your vote is in.
> results ballot box
[Juniper's ballot box] Mira has voted most: 1.
  1. The last word each visitor said, keyed by who: (put! self :last (assoc (:last self) who (last (words text)))) in an :on-hear.
  1. Made again with :state {:guests [] :opened 0}, the guest log keeps the state it had: :opened isn't there, and code that reads it should say (or (:opened self) 0). Chapter 18 shows it.

A small game #

  1. A bigger game: a second verb start-big that puts 21 in the player's game, the same as start with another number. Since 21 leaves a remainder of 1 when divided by 4, the pile can win it from the first move.
  1. Games played in all: a key :played in the state, read with (or (:played self) 0), and one added in over.
  1. A coin-guessing game:
#ans-game-3
(defobject guessing-coin
  {:name "Guessing coin"
   :state {:tossed {}}
   :verbs {:toss (fn [self who]
                   (put! self :tossed (assoc (:tossed self) who (rand-nth ["heads" "tails"])))
                   (say self "It's in the air. Say heads or tails to the guessing coin."))}
   :on-hear (fn [self who text]
              (when (:spoken-to self)
                (if-let [side (get (:tossed self) who)]
                  (let [guess (first (filter #{"heads" "tails"} (words (lower text))))]
                    (when guess
                      (put! self :tossed (dissoc (:tossed self) who))
                      (say self (if (= guess side) (str "It's " side ". Well guessed.") (str "It's " side ". Not this time.")))))
                  (say self "Toss me first: toss guessing coin."))))})
Played
> toss guessing coin
[Juniper's guessing coin] It's in the air. Say heads or tails to the guessing coin.
> say to the guessing coin: heads
[Juniper's guessing coin] It's …. …
  1. Winners best first: (sort-by (fn [[k v]] (- v)) (:wins self)), then the same join as before.

Pictures and dances #

  1. A plus sign:
#ans-designs-1
(design "plus" 7 7
        {:g "#3f8f5a"}
        ["...g..."
         "...g..."
         "...g..."
         "ggggggg"
         "...g..."
         "...g..."
         "...g..."])
  1. Stripes from a function:
#ans-designs-2
(defn stripe [y width] (join "" (repeat width (if (even? y) "r" "w"))))
(apply design (check "stripes" :draw_design
                     {:name "stripes"
                      :palette {:r "#c8553d" :w "#f6ecd6"}
                      :rows (mapv (fn [y] (stripe y 9)) (range 5))}))
  1. A rainy-day dance:
#ans-designs-3
(defdance :rainy-day
  [[:sway {:how :sleepily}] :tiptoe :look [:pause {:how :dreamily}] :hop :hop [:twirl {:how :slowly}] :bow]
  "Rainy day")
  1. The porch step backwards, from a plan sent after the porch step was made:
#ans-designs-4-first
(defdance :porch-step [:sway :hop :twirl :bow] "Porch step")
#ans-designs-4
(apply defdance (check "backwards" :make_dance
                       {:name "step-back" :moves (vec (reverse (own :dance "porch-step")))}))

The house as a form #

  1. Five degrees steeper:
#ans-house-1
(let [pitch (:pitch (-> (house) :masses first :roof))]
  (apply roof (check "steeper" :roof {:pitch (+ pitch 5)})))
  1. Every side:
#ans-house-2
(window-box :all ["lavender" "butter yellow"])
  1. A door of chance:
#ans-house-3
(apply paint (check "the door" :paint {:what "door" :shade (rand-nth ["moss" "rose" "slate blue"])}))
  1. A third part:
#ans-house-4
(house "Juniper's"
       {:part :mass :id "main" :at [3 3] :size [7 6] :stories 1}
       {:part :mass :id "wing" :at [10 4] :size [3 4] :stories 1}
       {:part :mass :id "nook" :at [1 5] :size [2 3] :stories 1})
(map :id (:masses (house)))

Living with others #

  1. Make it again with :open true, and say so to the neighbour; with a key they read it with source <toy> of <you>.
  2. With a key, source <toy> of <name>; through a chat app, go to theirs and check with what toy_lisp.
  3. :after {:by "<their handle>" :toy "<their toy>"} in your toy's map, with a key. They hear of it.
  4. The refusal names paint, and lists what a toy's code can use: its state, its words, its maker's colours, the weather and the language's helpers.

Limits, errors and refusals #

  1. nth with a value for past the end:
#ans-limits-1
(nth [] 0 "none")
It answers
"none"
  1. A cond on a missing count stops at (> (:count m) 3); (> (or (:count m) 0) 3) doesn't:
#ans-limits-2
(let [m {}]
  (cond (> (or (:count m) 0) 3) "many"
        :else "few"))
It answers
"few"
  1. Divide only when there's something to divide by: (if (zero? n) "Not yet." (str (/ total n))).
  1. (vec (range 200000)) makes 200,000 items at a step each, so it runs out of fuel:
Refused#ans-limits-4
(vec (range 200000))
It answers
Out of fuel: the script stopped at 100000 steps. Nothing was changed.

Style #

These are for you to try on your own toys; there's no one answer. A good test of each is to read the toy aloud, as a neighbour would.

Written by Claude, Plazmi's writer, for Plazmi, a service of Manatee Media LLC. Text © 2026 Manatee Media LLC. The examples are free to copy, change and run.