on master help sign in
Updating caesar.md for clarity (#907)

Replace arm names w/ slightly more descriptive names. Replace faces w/ slightly more descriptive faces. Remove key argument from operate arm where it was not doing anything. Add some extended description in various locations. Added casting to the code for most arms. Updated one exercise to add complexity (some students were just extending alpha arm w/ symbols)

commita66668bae4e9d3a9de331d08618937b5b184e6ce authorrabsef-bicrym <52549148+rabsef-bicrym@users.noreply.github.com> dateWed 15 Jul 2020, 12:01 (6 years ago) parentd53c750
1 file changed+182−135
tutorials/hoon/caesar.md+182−135
tutorials/hoon/caesar.md+182−135
@@ -31,51 +31,57 @@ demonstrates important principles that more laconic code would not. Save it as
3131
3232```hoon
3333!:
34−|= [msg=tape key=@ud]
34+|= [msg=tape steps=@ud]
3535=<
3636=. msg (cass msg)
37−:- (shift msg key)
38−(unshift msg key)
37+:- (shift msg steps)
38+(unshift msg steps)
39+
3940|%
4041++ alpha "abcdefghijklmnopqrstuvwxyz"
4142++ shift
42− |= [message=tape key=@ud]
43− (operate message key (encoder key))
43+ |= [message=tape shift-steps=@ud]
44+ ^- tape
45+ (operate message (encoder shift-steps))
4446++ unshift
45− |= [message=tape key=@ud]
46− (operate message key (decoder key))
47+ |= [message=tape shift-steps=@ud]
48+ ^- tape
49+ (operate message (decoder shift-steps))
50+++ encoder
51+ |= [steps=@ud]
52+ ^- (map @t @t)
53+ =/ value-tape=tape (rotation alpha steps)
54+ (space-adder alpha value-tape)
55+++ decoder
56+ |= [steps=@ud]
57+ ^- (map @t @t)
58+ =/ value-tape=tape (rotation alpha steps)
59+ (space-adder value-tape alpha)
4760++ operate
48− |= [message=tape key=@ud encoder=(map @t @t)]
61+ |= [message=tape shift-map=(map @t @t)]
4962 ^- tape
5063 %+ turn message
5164 |= a=@t
52− (~(got by encoder) a)
53−++ encoder
54− |= [key=@ud]
55− =/ keytape=tape (rott alpha key)
56− (coder alpha keytape)
57−++ decoder
58− |= [key=@ud]
59− =/ keytape=tape (rott alpha key)
60− (coder keytape alpha)
61−++ coder
62− |= [a=tape b=tape]
63− (~(put by (zipper a b)) ' ' ' ')
64−++ zipper
65− |= [a=tape b=tape]
65+ (~(got by shift-map) a)
66+++ space-adder
67+ |= [key-position=tape value-result=tape]
68+ ^- (map @t @t)
69+ (~(put by (map-maker key-position value-result)) ' ' ' ')
70+++ map-maker
71+ |= [key-position=tape value-result=tape]
6672 ^- (map @t @t)
6773 =| chart=(map @t @t)
68− ?. =((lent a) (lent b))
74+ ?. =((lent key-position) (lent value-result))
6975 ~| %uneven-lengths !!
7076 |-
71− ?: |(?=(~ a) ?=(~ b))
77+ ?: |(?=(~ key-position) ?=(~ value-result))
7278 chart
73− $(chart (~(put by chart) i.a i.b), a t.a, b t.b)
74−++ rott
75− |= [m=tape n=@ud]
76− =/ length=@ud (lent m)
77− =+ s=(trim (mod n length) m)
78− (weld q.s p.s)
79+ $(chart (~(put by chart) i.key-position i.value-result), key-position t.key-position, value-result t.value-result)
80+++ rotation
81+ |= [my-alphabet=tape my-steps=@ud]
82+ =/ length=@ud (lent my-alphabet)
83+ =+ (trim (mod my-steps length) my-alphabet)
84+ (weld q p)
7985--
8086```
8187
@@ -87,6 +93,9 @@ that has been shifted left. It also converts any uppercase input into lowercase.
8793Try it out in the Dojo:
8894
8995```
96+> +caesar ["abcdef" 1]
97+["bcdefg" "zabcde"]
98+
9099> +caesar ["test" 2]
91100["vguv" "rcqr"]
92101
@@ -115,30 +124,30 @@ itself before reading the explanation.
115124
116125```hoon
117126!:
118−|= [msg=tape key=@ud]
127+|= [msg=tape steps=@ud]
119128=<
120129```
121130
122131The `!:` in the first line of the above code enables a full stack trace in the
123132event of an error.
124133
125−`|= [msg=tape key=@ud]` creates a [gate](/docs/glossary/gate/) that takes a cell. The head of this cell
134+`|= [msg=tape steps=@ud]` creates a [gate](/docs/glossary/gate/) that takes a cell. The head of this cell
126135is a `tape`, which is a string type that's a list of `cord`s. Tapes are represented
127136as text surrounded by double-quotes, such as this: `"a tape"`. We give this input
128137tape the face `msg`. The tail of our cell is a `@ud` -- an unsigned decimal [atom](/docs/glossary/atom/)
129−-- that we give the face `key`.
138+-- that we give the face `steps`.
130139
131140`=<` is the rune that evaluates its first child expression with respect to its
132141second child expression as the subject. In this case, we evaluate the
133142expressions in the code chunk below against the [core](/docs/glossary/core/) declared later, which
134143allows us reference the core's contained [arms](/docs/glossary/arm/) before they are defined. Without
135−`=<`, we would need to put the code chunk below at the bottom of our program.
144+`=<`, we would need to put the code chunk below at the bottom of our program. In Hoon, as previously
145+stated, we always want to keep the longer code towards the bottom of our programs - `=<` helps us do that.
136146
137147```hoon
138148=. msg (cass msg)
139−:- (shift msg key)
140−(unshift msg key)
141−|%
149+:- (shift msg steps)
150+(unshift msg steps)
142151```
143152
144153`=. msg (cass msg)` changes the input string `msg` to lowercases. `=.` changes
@@ -146,137 +155,165 @@ the leg of the subject to something else. In our case, the leg to be changed is
146155`msg`, and the thing to replace it is `(cass msg)`. `cass` is a standard-library
147156gate that converts uppercase letters to lowercase.
148157
149−`:- (shift msg key)` and `(unshift msg key)` simply composes a
150−cell of a right-shifted cipher and a left-shifted cell. This is the final output
151−of our generator.
158+`:- (shift msg steps)` and `(unshift msg steps)` simply composes a
159+cell of a right-shifted cipher and a left-shifted cipher of our original message.
160+We will see how this is done using the core described below, but this is the final
161+output of our generator.
152162
153−`|%` creates a core.
163+`|%` creates a `core`, the second child of `=<`. Everything after `|%` is part of that
164+second child `core`, and will be used as the subject of the first child of `=<`, described
165+above. The various parts, or `arm`s, of the `core` are denoted by `++` beneath it, for
166+instance:
154167
155168```hoon
156− ++ rott
157− |= [m=tape n=@ud]
158− =/ length=@ud (lent m)
159− =+ s=(trim (mod n length) m)
160− (weld q.s p.s)
169+ ++ rotation
170+ |= [my-alphabet=tape my-steps=@ud]
171+ =/ length=@ud (lent my-alphabet)
172+ =+ (trim (mod my-steps length) my-alphabet)
173+ (weld q p)
161174```
162175
163−The `rott` arm takes takes a specified number of characters off of a tape and
164−puts them on the end of the tape.
176+The `rotation` arm takes takes a specified number of characters off of a tape and
177+puts them on the end of the tape. We're going to use this to create our shifted alphabet,
178+based on the number of `steps` given as an argument to our gate.
165179
166−`|= [m=tape n=@ud]` creates a gate that takes two arguments: `m`, a `tape`,
167−and `n`, a `@ud`.
180+`|= [my-alphabet=tape my-steps=@ud]` creates a gate that takes two arguments: `my-alphabet`, a `tape`,
181+and `my-steps`, a `@ud`.
168182
169−`=/ length=@ud (lent m)` stores the length of `m` to make the following
183+`=/ length=@ud (lent my-alphabet)` stores the length of `my-alphabet` to make the following
170184code a little clearer.
171185
172186`trim` is a a gate from the standard library that splits a tape at into two
173−parts at a specified position. So `=+ s=(trim (mod n length) m)` splits the
174−tape `m` into two parts, `p` and `q`, and stores it in `s`. We call the modulus
175−operation `mod` to make sure that we split our `tape` at the correct place, even
176−if `n` is larger than `length`.
187+parts at a specified position. So `=+ (trim (mod my-steps length) my-alphabet)` splits the
188+tape `my-alphabet` into two parts, `p` and `q`, which are now directly available in the subject.
189+We call the modulus operation `mod` to make sure that the point at which we split our `tape` is
190+a valid point inside of `my-alphabet` even if `my-steps` is greater than `length`, the length of
191+`my-alphabet`. Try trim in the dojo:
192+
193+```
194+> (trim 2 "abcdefg")
195+[p="ab" q="cdefg"]
196+
197+> (trim 4 "yourbeard")
198+[p="your" q="beard"]
199+```
177200
178−`(weld q.s p.s)` uses `weld`, which combines two strings into one. In this
179−instance, we are switching the two parts: the second part `q.s` is welded to the
180−front, and the first part `p.s` is welded to the back.
201+`(weld q p)` uses `weld`, which combines two strings into one. Remember that `trim` has given us
202+a split version of `my-alphabet` with `p` being the front half that was split off of `my-alphabet`
203+and `q` being the back half. Here we are welding the two parts back together, but in reverse order:
204+the second part `q` is welded to the front, and the first part `p` is welded to the back.
181205
182206```hoon
183− ++ zipper
184− |= [a=tape b=tape]
207+ ++ map-maker
208+ |= [key-position=tape value-result=tape]
185209 ^- (map @t @t)
186210 =| chart=(map @t @t)
187− ?. =((lent a) (lent b))
188− ~| %uneven-lengths !!
211+ ?. =((lent key-position) (lent value-result))
212+ ~| %uneven-lengths !!
189213 |-
190− ?: |(?=(~ a) ?=(~ b))
191− chart
192− $(chart (~(put by chart) i.a i.b), a t.a, b t.b)
214+ ?: |(?=(~ key-position) ?=(~ value-result))
215+ chart
216+ $(chart (~(put by chart) i.key-position i.value-result), key-position t.key-position, value-result t.value-result)
193217```
194218
195−The `zipper` arm takes two tapes and creates a `map` out of them -- an
196−association between their elements.
197−
198−So then if `tapes` are easier to manipulate, why then are we producing `cords`?
199−In this case what we are going to to be doing is mapping a single element of a `
200−tape` to another element of a `tape`, which as we have established as being a
201−`cord`. This will simplify our use of this method and we don't need to
202−manipulate the keys or values, simply look them up.
219+The `map-maker` arm, as the name implies, takes two tapes and creates a [`map`](/docs/reference/library/2o/#map) out of them.
220+A `map` is a type equivalent to a dictionary in other languages: it's a data structure that
221+associates a key with a value. If, for example, we wanted to have an association
222+between `a` and 1 and `b` and 2, we could use a `map`.
203223
204224`|= [a=tape b=tape]` builds a gate that takes two tapes, `a` and `b`, as its
205225sample.
206226
207−`^- (map @t @t)` casts the gate to a `map` with a `cord` key and a `cord`
208−value. A `map` is a type equivalent to a dictionary in other languages: it's a
209−data structure that associates a key with a value. If, for example, we wanted
210−to have an association between `a` and 1 and `b` and 2, we could use a `map`.
227+`^- (map @t @t)` casts the gate to a `map` with a `cord` (or `@t`) key and a `cord`
228+value.
229+
230+You might wonder, if our gate in this arm takes `tape`s, why then are we producing
231+a map of `cord` keys and values?
232+
233+As we discussed earlier, a `tape` is a list of `cord`s. In this case what we are going to do
234+is map a single element of a `tape` (either our alphabet or shifted-alphabet) to an element of
235+a different `tape` (either our shifted-alphabet or our alphabet). This pair will therefore be
236+a pair of `cord`s. When we go to use this `map` to convert our incoming `msg`, we will take
237+each element (`cord`) of our `msg` `tape`, use it as a `key` when accessing our `map` and get
238+the corresponding `value` from that position in the `map`. This is how we're going to encode
239+or decode our `msg` `tape`.
211240
212241`=| chart=(map @t @t)` adds a [noun](/docs/glossary/noun/) to the subject with the default value of
213242the `(map @t @t)` type, and gives that noun the face `chart`.
214243
215−`?. =((lent a) (lent b))` checks if the two tapes are the same length. If not,
216−the program crashes.
244+`?. =((lent key-position) (lent value-result))` checks if the two `tape`s are the same length. If not,
245+the program crashes with an error message of `%uneven-lengths`, using `|~ %uneven-lengths !!`.
217246
247+If the two `tape`s are of the same length, we continue on to create a trap.
218248`|-` creates a [trap](/docs/glossary/trap/), a gate that is called immediately.
219249
220−`?: |(?=(~ a) ?=(~ b))` checks if either tape is empty. If this is true, we
221−the program is finished and can return `chart`, the the `map` that we have been
250+`?: |(?=(~ key-position) ?=(~ value-result))` checks if either `tape` is empty. If this is true, the
251+`map-maker` arm is finished and can return `chart`, the the `map` that we have been
222252creating.
223253
224−If the above test finds that its condition to be false, we trigger a recursion
254+If the above test finds that the `tape`s are not empty, we trigger a recursion
225255that constructs our `map`: `$(chart (~(put by chart) i.a i.b), a t.a, b t.b)`.
226−This code recursively adds an entry in our `map` where the head of the tape `a`
227−maps to the value of the head of tape `b` with `~(put by chart)`, our calling
228−of the `put` arm of the `by` map-engine core. The recursion also "consumes"
229−those heads with every iteration by changing `a` and `b` to their tails.
230−
231−Perhaps now you can understand why this arm is named `zipper`. It's putting two
232−tapes together by matching pieces from each side, something akin to a jacket zipper.
233−
234−We have three related arms to look at next, `coder`, `encoder`, and
235−`decoder`. `coder` is the foundation of the other two, so we'll look at it
256+This code recursively adds an entry in our `map` where the head of the `tape` `a`
257+maps to the value of the head of `tape` `b` with `~(put by chart)`, our calling
258+of the `put` arm of the `by` map-engine core (note that `~(<wing> <door> <sample>`) is
259+a shorthand for `%~ <wing> <door> <sample>` (see the [Calls % ('cen')](/docs/reference/hoon-expressions/rune/cen/#censig)
260+documentation for more information). The recursion also "consumes"
261+those heads with every iteration by changing `a` and `b` to their tails using `a t.a, b t.b`.
262+
263+We have three related arms to look at next, `decoder`, `encoder`, and
264+`space-adder`. `space-adder` is required for the other two, so we'll look at it
236265first.
237266
238267```hoon
239− ++ coder
240− |= [a=tape b=tape]
241− (~(put by (zipper a b)) ' ' ' ')
268+ ++ space-adder
269+ |= [key-position=tape value-result=tape]
270+ ^- (map @t @t)
271+ (~(put by (map-maker key-position value-result)) ' ' ' ')
242272```
243273
244−`|= [a=tape b=tape]` creates a gate that takes two `tapes`.
274+`|= [key-position=tape value-result=tape]` creates a gate that takes two `tapes`.
245275
246−We use `put by` on the next line, giving it a `map` produced by the `zipper`
247−arm that we created before. This adds an entry to the map where the space
248−character (called `ace`) simply maps to itself. This is done to simplify
249−the handling of spaces in `tapes` we want to encode, since we don't want to
276+We use the `put` arm of the `by` core on the next line, giving it a `map` produced
277+by the `map-maker` arm that we created before as its sample. This adds an entry to the
278+map where the space character (called `ace`) simply maps to itself. This is done to
279+simplify the handling of spaces in `tapes` we want to encode, since we don't want to
250280shift them.
251281
252282```hoon
253283 ++ encoder
254− |= [key=@ud]
255− =/ keytape=tape (rott alpha key)
256− (coder alpha keytape)
284+ |= [steps=@ud]
285+ ^- (map @t @t)
286+ =/ value-tape=tape (rotation alpha steps)
287+ (space-adder alpha value-tape)
257288 ++ decoder
258− |= [key=@ud]
259− =/ keytape=tape (rott alpha key)
260− (coder keytape alpha)
289+ |= [steps=@ud]
290+ ^- (map @t @t)
291+ =/ key-tape=tape (rotation alpha steps)
292+ (space-adder key-tape alpha)
261293```
262294
263−`encoder` and `decoder` are implemented in terms of the `coder` arm. These gates
264−are essentially identical, with the arguments reversed. They simplify the two
265−common transactions you want to do in this program: producing `maps` that we can
266−use to encode and decode messages.
295+`encoder` and `decoder` utilize the `rotation` and `space-adder` arms. These gates
296+are essentially identical, with the arguments passed to `rotation` reversed. They
297+simplify the two common transactions you want to do in this program: producing `maps`
298+that we can use to encode and decode messages.
299+
300+In both cases, we create a gate that accepts a `@ud` named `steps`.
267301
268−In both cases, we create a gate that accepts a `@ud` named `key`.
302+In `encoder`:
303+`=/ value-tape=tape (rotation alpha steps)` creates a `value-tape` noun by calling `rotation`
304+on `alpha`. `alpha` is our arm which contains a `tape` of the entire alphabet. The
305+`value-tape` will be the list of `value`s in our `map`.
269306
270−`=/ keytape=tape (rott alpha key)` creates a `keytape` noun by calling `rott`
271−on `alpha` our `key` input. `alpha` is our arm which contains a `tape` of the
272−entire alphabet.
307+In `decoder`:
308+`=/ key-tape (rotation alpha steps)` does the same work, but when passed to `space-adder`
309+it will be the list of `key`s in our `map`.
273310
274−`(coder alpha keytape)`, for `encoder`, and `(coder keytape alpha)`, for
311+`(space-adder alpha value-tape)`, for `encoder`, and `(space-adder key-tape alpha)`, for
275312`decoder`, produce a `map` that has the first argument as the keys and the
276313second as the values.
277314
278−If our two inputs to `coder` were `"abcdefghijklmnopqrstuvwxyz"` and
279−`"bcdefghijklmnopqrstuvwxyza"` We would get a `map` where `'a'` maps to `'b'`,
315+If our two inputs to `space-adder` were `"abcdefghijklmnopqrstuvwxyz"` and
316+`"bcdefghijklmnopqrstuvwxyza"`, we would get a `map` where `'a'` maps to `'b'`,
280317`'b'` to `'c'` and so on. By doing this we can produce a `map` that gives us a
281318translation between the alphabet and our shifted alphabet, or vice versa.
282319
@@ -285,30 +322,32 @@ walked through.
285322
286323```hoon
287324 ++ shift
288− |= [message=tape key=@ud]
289− (operate message key (encoder key))
325+ |= [message=tape shift-steps=@ud]
326+ ^- tape
327+ (operate message (encoder shift-steps))
290328 ++ unshift
291− |= [message=tape key=@ud]
292− (operate message key (decoder key))
329+ |= [message=tape shift-steps=@ud]
330+ ^- tape
331+ (operate message (decoder shift-steps))
293332```
294333
295334Both `shift` and `unshift` take two arguments: our `message`, the `tape` that we
296−want to manipulate; and our `key`, the value that we want to shift our message
297−by.
335+want to manipulate; and our `shift-steps`, the number of positions of the alphabet
336+by which we want to shift our message.
298337
299338`shift` is for encoding, and `unshift` is for decoding. Thus, `shift` calls the
300−`operate` arm with `(operate message key (encoder key))`, and `unshift` makes
301−that call with `(operate message key (decoder key))`. These both produce the
302−final output of the core, to be called in the form of `(shift msg key)`
303−and `(unshift msg key)` at the bottom of the program.
339+`operate` arm with `(operate message (encoder shift-steps))`, and `unshift` makes
340+that call with `(operate message (decoder shift-steps))`. These both produce the
341+final output of the core, to be called in the form of `(shift msg steps)`
342+and `(unshift msg steps)` in the cell being created at the beginning of our code.
304343
305344```hoon
306−++ operate
307− |= [message=tape key=@ud encoder=(map @t @t)]
308− ^- tape
309− %+ turn message
310− |= a=@t
311− (~(got by encoder) a)
345+ ++ operate
346+ |= [message=tape shift-map=(map @t @t)]
347+ ^- tape
348+ %+ turn message
349+ |= a=@t
350+ (~(got by shift-map) a)
312351```
313352
314353`operate` produces a `tape`. The `%+` rune allows us to pull an arm
@@ -316,6 +355,12 @@ with a pair sample. The arm we are going to pull is `turn`. This arm
316355takes two arguments, a `list` and a `gate` to apply to each element of the
317356`list`.
318357
358+In this case, the `gate` we are applying to our `message` uses the `got` arm
359+of the `by` door with our `shift-map` as the sample (which is either the standard alphabet
360+for keys, and the shifted alphabet for values, or the other way, depending on
361+whether we are encoding or decoding) to look up each `cord` in our `message`, one by one
362+and replace it with the `value` from our `map` (either the encoded or decoded version).
363+
319364If we then give our arm Caesar's famous statement, and get our left- and
320365right-ciphers.
321366
@@ -340,8 +385,10 @@ look for the legible result.
3403851. Take the example generator and modify it to add a second layer of shifts.
341386
3423872. Extend the example generator to allow for use of characters other than a-z.
388+ Make it shift the new characters independently of the alpha characters, such
389+ that punctuation is only encoded as other punctuation marks.
343390
344−3. Build a gate that can take a Caesar shifted `tape` and produce
345−possible unshifted `tapes`.
391+3. Build a gate that can take a Caesar shifted `tape` and produce all
392+ possible unshifted `tapes`.
346393
3473944. Modify the example generator into a `%say` generator.