Word frequency
Count words in a fixed sentence after lowercasing it and removing periods. Empty pieces are skipped, and a separate vocabulary array determines the reporting order.
let text = "Small steps. Small programs. Clear steps."let words = text.to_lower().replace(".", "").split(" ")let counts = Map[Str, Int].new()for word in words { if word == "" { cont } let previous = counts.get(word).unwrap_or(0) counts[word] = previous + 1}for word in ["small", "steps", "programs", "clear"] { print("{word}: {counts[word]}")}print("distinct words: {counts.len()}")Original example: checked output and syntax tree
These records belong to the original downloadable program. Run the editor above to see results for your changes.
small: 2 steps: 2 programs: 1 clear: 1 distinct words: 4
Original AST
[
{
"node": {
"Let": {
"pattern": {
"node": {
"Binding": {
"node": "text",
"span": [
4,
8
]
}
},
"span": [
4,
8
]
},
"type_ann": null,
"value": {
"node": {
"Str": "Small steps. Small programs. Clear steps."
},
"span": [
11,
54
]
}
}
},
"span": [
0,
54
]
},
{
"node": {
"Let": {
"pattern": {
"node": {
"Binding": {
"node": "words",
"span": [
59,
64
]
}
},
"span": [
59,
64
]
},
"type_ann": null,
"value": {
"node": {
"Call": {
"callee": {
"node": {
"Attr": {
"object": {
"node": {
"Call": {
"callee": {
"node": {
"Attr": {
"object": {
"node": {
"Call": {
"callee": {
"node": {
"Attr": {
"object": {
"node": {
"Name": "text"
},
"span": [
67,
71
]
},
"name": "to_lower",
"name_span": [
72,
80
]
}
},
"span": [
67,
80
]
},
"args": [],
"args_span": [
80,
82
]
}
},
"span": [
67,
82
]
},
"name": "replace",
"name_span": [
83,
90
]
}
},
"span": [
67,
90
]
},
"args": [
{
"node": {
"Str": "."
},
"span": [
91,
94
]
},
{
"node": {
"Str": ""
},
"span": [
96,
98
]
}
],
"args_span": [
90,
99
]
}
},
"span": [
67,
99
]
},
"name": "split",
"name_span": [
100,
105
]
}
},
"span": [
67,
105
]
},
"args": [
{
"node": {
"Str": " "
},
"span": [
106,
109
]
}
],
"args_span": [
105,
110
]
}
},
"span": [
67,
110
]
}
}
},
"span": [
55,
110
]
},
{
"node": {
"Let": {
"pattern": {
"node": {
"Binding": {
"node": "counts",
"span": [
115,
121
]
}
},
"span": [
115,
121
]
},
"type_ann": null,
"value": {
"node": {
"Call": {
"callee": {
"node": {
"Attr": {
"object": {
"node": {
"Item": {
"object": {
"node": {
"Name": "Map"
},
"span": [
124,
127
]
},
"args": [
{
"node": {
"Name": "Str"
},
"span": [
128,
131
]
},
{
"node": {
"Name": "Int"
},
"span": [
133,
136
]
}
]
}
},
"span": [
124,
137
]
},
"name": "new",
"name_span": [
138,
141
]
}
},
"span": [
124,
141
]
},
"args": [],
"args_span": [
141,
143
]
}
},
"span": [
124,
143
]
}
}
},
"span": [
111,
143
]
},
{
"node": {
"Expr": {
"node": {
"For": {
"pattern": {
"node": {
"Binding": {
"node": "word",
"span": [
148,
152
]
}
},
"span": [
148,
152
]
},
"iter_expr": {
"node": {
"Name": "words"
},
"span": [
156,
161
]
},
"body": [
{
"node": {
"Expr": {
"node": {
"If": {
"cond": {
"node": {
"BinOp": {
"op": "Eq",
"left": {
"node": {
"Name": "word"
},
"span": [
171,
175
]
},
"right": {
"node": {
"Str": ""
},
"span": [
179,
181
]
}
}
},
"span": [
171,
181
]
},
"then_body": [
{
"node": {
"Cont": {
"keyword": [
184,
188
]
}
},
"span": [
184,
188
]
}
],
"else_body": null
}
},
"span": [
168,
190
]
}
},
"span": [
168,
190
]
},
{
"node": {
"Let": {
"pattern": {
"node": {
"Binding": {
"node": "previous",
"span": [
199,
207
]
}
},
"span": [
199,
207
]
},
"type_ann": null,
"value": {
"node": {
"Call": {
"callee": {
"node": {
"Attr": {
"object": {
"node": {
"Call": {
"callee": {
"node": {
"Attr": {
"object": {
"node": {
"Name": "counts"
},
"span": [
210,
216
]
},
"name": "get",
"name_span": [
217,
220
]
}
},
"span": [
210,
220
]
},
"args": [
{
"node": {
"Name": "word"
},
"span": [
221,
225
]
}
],
"args_span": [
220,
226
]
}
},
"span": [
210,
226
]
},
"name": "unwrap_or",
"name_span": [
227,
236
]
}
},
"span": [
210,
236
]
},
"args": [
{
"node": {
"Int": "0"
},
"span": [
237,
238
]
}
],
"args_span": [
236,
239
]
}
},
"span": [
210,
239
]
}
}
},
"span": [
195,
239
]
},
{
"node": {
"Assign": {
"target": {
"Item": {
"object": {
"node": {
"Name": "counts"
},
"span": [
244,
250
]
},
"args": [
{
"node": {
"Name": "word"
},
"span": [
251,
255
]
}
]
}
},
"value": {
"node": {
"BinOp": {
"op": "Add",
"left": {
"node": {
"Name": "previous"
},
"span": [
259,
267
]
},
"right": {
"node": {
"Int": "1"
},
"span": [
270,
271
]
}
}
},
"span": [
259,
271
]
}
}
},
"span": [
244,
271
]
}
]
}
},
"span": [
144,
273
]
}
},
"span": [
144,
273
]
},
{
"node": {
"Expr": {
"node": {
"For": {
"pattern": {
"node": {
"Binding": {
"node": "word",
"span": [
278,
282
]
}
},
"span": [
278,
282
]
},
"iter_expr": {
"node": {
"ArrLit": {
"elements": [
{
"node": {
"Str": "small"
},
"span": [
287,
294
]
},
{
"node": {
"Str": "steps"
},
"span": [
296,
303
]
},
{
"node": {
"Str": "programs"
},
"span": [
305,
315
]
},
{
"node": {
"Str": "clear"
},
"span": [
317,
324
]
}
]
}
},
"span": [
286,
325
]
},
"body": [
{
"node": {
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "print"
},
"span": [
332,
337
]
},
"args": [
{
"node": {
"Interp": {
"parts": [
{
"Expr": {
"node": {
"Name": "word"
},
"span": [
340,
344
]
}
},
{
"Lit": ": "
},
{
"Expr": {
"node": {
"Item": {
"object": {
"node": {
"Name": "counts"
},
"span": [
348,
354
]
},
"args": [
{
"node": {
"Name": "word"
},
"span": [
355,
359
]
}
]
}
},
"span": [
348,
360
]
}
}
]
}
},
"span": [
338,
362
]
}
],
"args_span": [
337,
363
]
}
},
"span": [
332,
363
]
}
},
"span": [
332,
363
]
}
]
}
},
"span": [
274,
365
]
}
},
"span": [
274,
365
]
},
{
"node": {
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "print"
},
"span": [
366,
371
]
},
"args": [
{
"node": {
"Interp": {
"parts": [
{
"Lit": "distinct words: "
},
{
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Attr": {
"object": {
"node": {
"Name": "counts"
},
"span": [
390,
396
]
},
"name": "len",
"name_span": [
397,
400
]
}
},
"span": [
390,
400
]
},
"args": [],
"args_span": [
400,
402
]
}
},
"span": [
390,
402
]
}
}
]
}
},
"span": [
372,
404
]
}
],
"args_span": [
371,
405
]
}
},
"span": [
366,
405
]
}
},
"span": [
366,
405
]
}
]Why the lookup is optional
Section titled “Why the lookup is optional”On a word’s first occurrence, the map has no entry. get(word) returns Opt[Int], and unwrap_or(0) supplies the missing count. Updating the map then uses the same expression for both new and repeated words.
The final reporting loop uses indexing because every listed word is known to occur. For an unknown report vocabulary, use get there too.
This is a deliberately small tokenizer: it only normalizes periods, case, and spaces. It neither sorts words nor handles general punctuation.
Read the full tutorial to build the program one decision at a time, or consult the collection APIs.