# Text, lists and maps

From A Little Lisp for a Small World, the Plazmi Lisp book (https://plazmi.gg/book). Checked against Plazmi Lisp version 2, 12 November 2026. The whole book as one file: https://plazmi.gg/book.md

Most of what a toy says is text, and most of what it remembers is a list or a map. This chapter is a tour of the words for each. You don't need to learn them all at once: read it through, and come back when you need one.

## Text

`str` joins values into text, writing each as a person would read it. `count` says how many characters a text has, and `subs` takes part of it, counting from 0:

```clojure
(str "Ting" "." " " 3 " rings")
(count "garden")
(subs "garden" 0 3)
(subs "garden gate" 7)
```

```text
"Ting. 3 rings"
6
"gar"
"gate"
```

`(subs t a b)` is the characters from place `a` up to, not including, place `b`; with no `b`, to the end.

These words change or test text. Each answers a new text, or `true` or `false`:

```clojure
(upper "ting")
(lower "Hello, Bell")
(trim "  hello  ")
(includes? "a lovely garden" "garden")
(starts-with? "garden gate" "garden")
(ends-with? "garden gate" "gate")
(blank? "   ")
(replace-all "a-b-c" "-" " ")
```

```text
"TING"
"hello, bell"
"hello"
true
true
true
true
"a b c"
```

`replace-all` replaces every copy of plain text: the second text is never a pattern.

## Text into lists, and back

`words` splits text at its spaces, `split` at any plain text you give, and `join` puts a list back together with a separator between:

```clojure
(words "the  lavender came up")
(split "rose,sage,moss" ",")
(join ", " ["rose" "sage" "moss"])
(join " · " [1 2 3])
```

```text
["the" "lavender" "came" "up"]
["rose" "sage" "moss"]
"rose, sage, moss"
"1 · 2 · 3"
```

`join` takes the separator first and the list second.

## Numbers in text

`format` writes numbers into text by a pattern: `%s` for any value, `%d` for a whole number, and `%.2f` for a decimal with two places. `parse-long` and `parse-double` read a number out of text, and answer `nil` when the text isn't one:

```clojure
(format "%d rings" 3)
(format "%s and %s" "rose" "sage")
(format "%.2f metres" 2.456)
(parse-long "42")
(parse-long "forty")
(parse-double "2.5")
```

```text
"3 rings"
"rose and sage"
"2.46 metres"
42
nil
2.5
```

## Keywords and text

`name` gives a keyword's text, and `keyword` makes a keyword from text:

```clojure
(name :rain)
(keyword "rain")
```

```text
"rain"
:rain
```

## Patterns

A pattern, `#"…"`, describes text to look for. `re-find` answers the first piece of text that fits, `re-seq` every piece, and `re-matches` the text only when all of it fits:

```clojure
(re-find #"[0-9]+" "it rang 12 times")
(re-seq #"[a-z]+" "Rose, sage and moss")
(re-matches #"[a-z]+" "bell")
(re-matches #"[a-z]+" "bell 2")
```

```text
"12"
("ose" "sage" "and" "moss")
"bell"
nil
```

`[a-z]+` means one or more small letters, so the capital R of "Rose" isn't part of a match. Patterns are useful, but for most words a toy hears, `includes?` and `words` are plainer, and chapter 10 has a word, `match`, made for it.

## Lists

These words read a list. Places are counted from 0:

```clojure
(first ["rose" "sage" "moss"])
(second ["rose" "sage" "moss"])
(last ["rose" "sage" "moss"])
(nth ["rose" "sage" "moss"] 1)
(nth ["rose" "sage" "moss"] 5 "none")
(count ["rose" "sage" "moss"])
(rest ["rose" "sage" "moss"])
(empty? [])
```

```text
"rose"
"sage"
"moss"
"sage"
"none"
3
("sage" "moss")
true
```

`nth` with a third value answers that value when the place is past the end. Without one, a place past the end stops the plan (chapter 17).

These answer new lists:

```clojure
(conj ["rose" "sage"] "moss")
(cons "fern" ["rose" "sage"])
(concat ["rose"] ["sage" "moss"])
(take 2 ["rose" "sage" "moss"])
(drop 2 ["rose" "sage" "moss"])
(take-last 2 ["rose" "sage" "moss"])
(reverse [1 2 3])
(sort [3 1 2])
(distinct [1 1 2 1])
(vec (range 5))
```

```text
["rose" "sage" "moss"]
("fern" "rose" "sage")
("rose" "sage" "moss")
("rose" "sage")
("moss")
("sage" "moss")
(3 2 1)
(1 2 3)
(1 2)
[0 1 2 3 4]
```

Some answers print in round brackets: they are lists all the same, of the kind Lisp makes as it goes. `vec` turns one into square brackets when you want to keep it. `conj` adds to the end of a square-bracket list; `cons` adds to the front.

`range` counts: `(range 5)` is 0 to 4, `(range 2 5)` is 2, 3 and 4, and `(range 0 10 3)` goes up in threes.

## Maps

A map holds values under keys. These read one:

```clojure
(get {:rings 4 :colour "rose"} :colour)
(:rings {:rings 4 :colour "rose"})
(get {:rings 4} :chimes 0)
(keys {:rings 4 :colour "rose"})
(vals {:rings 4 :colour "rose"})
(contains? {:rings 4} :rings)
(get-in {:bell {:rings 4}} [:bell :rings])
(count {:rings 4 :colour "rose"})
```

```text
"rose"
4
0
(:rings :colour)
(4 "rose")
true
4
2
```

And these answer new maps:

```clojure
(assoc {:rings 4} :colour "rose")
(dissoc {:rings 4 :colour "rose"} :colour)
(update {:rings 4} :rings inc)
(merge {:rings 4} {:colour "rose"})
(select-keys {:rings 4 :colour "rose" :open true} [:rings :open])
(assoc-in {} [:bell :rings] 1)
(update-in {:bell {:rings 1}} [:bell :rings] inc)
(zipmap [:rose :sage] [3 5])
```

```text
{:rings 4, :colour "rose"}
{:rings 4}
{:rings 5}
{:rings 4, :colour "rose"}
{:rings 4, :open true}
{:bell {:rings 1}}
{:bell {:rings 2}}
{:rose 3, :sage 5}
```

`update` takes a key and a word to apply to its value: `(update m :rings inc)` adds one to `:rings`. Keys are usually keywords, but text works too: `{"@mira" 2 "@cairn" 1}` is a map from names to counts, and chapter 12 uses one.

## Sets

A set holds each value once. `conj` adds, `disj` takes away, and `contains?` asks:

```clojure
(conj #{"rose"} "sage" "rose")
(disj #{"rose" "sage"} "rose")
(contains? #{"rose" "sage"} "sage")
(set ["rose" "sage" "rose"])
```

```text
#{"rose" "sage"}
#{"sage"}
true
#{"rose" "sage"}
```

A set prints its values in an order of its own. Use a set when what matters is whether something is there, and a list when order matters.

One trap: `contains?` on a list asks about places, not values. `(contains? ["rose"] 0)` is `true` because there is a place 0. To ask whether a list holds a value, make it a set first: `(contains? (set guests) "@mira")`.

## Try it

1. Answer `"BELL, CHIME, GONG"` from the list `["bell" "chime" "gong"]`.
2. From `"Mira says the tea is ready"`, answer how many words it has.
3. Start with `{:rings 0}` and answer a map with `:rings` 1 and `:last "@mira"`.
4. Answer the last two items of `(range 10)` as a square-bracket list.
5. Answer whether the text `"rain later"` mentions rain, and whether it starts with it.

## Recap

- Text: `str`, `count`, `subs`, `upper`, `lower`, `trim`, `includes?`, `starts-with?`, `ends-with?`, `replace-all`, `words`, `split`, `join`, `format`, `parse-long`.
- Lists: `first`, `rest`, `nth`, `count`, `conj`, `concat`, `take`, `drop`, `sort`, `range`, `vec`.
- Maps: `get`, `(:k m)`, `assoc`, `dissoc`, `update`, `merge`, `keys`, `vals`, `get-in`, `assoc-in`.
- Sets hold each value once; `contains?` asks about keys and places, not list values.

Next: making choices.


---

Before: [Names](https://plazmi.gg/book/names.md). Next: [Tests and choices](https://plazmi.gg/book/choices.md). Contents: https://plazmi.gg/book.md
