markov.xml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <!--
  2. Copyright 2011 The Go Authors. All rights reserved.
  3. Use of this source code is governed by a BSD-style
  4. license that can be found in the LICENSE file.
  5. -->
  6. <codewalk title="Generating arbitrary text: a Markov chain algorithm">
  7. <step title="Introduction" src="doc/codewalk/markov.go:/Generating/,/line\./">
  8. This codewalk describes a program that generates random text using
  9. a Markov chain algorithm. The package comment describes the algorithm
  10. and the operation of the program. Please read it before continuing.
  11. </step>
  12. <step title="Modeling Markov chains" src="doc/codewalk/markov.go:/ chain/">
  13. A chain consists of a prefix and a suffix. Each prefix is a set
  14. number of words, while a suffix is a single word.
  15. A prefix can have an arbitrary number of suffixes.
  16. To model this data, we use a <code>map[string][]string</code>.
  17. Each map key is a prefix (a <code>string</code>) and its values are
  18. lists of suffixes (a slice of strings, <code>[]string</code>).
  19. <br/><br/>
  20. Here is the example table from the package comment
  21. as modeled by this data structure:
  22. <pre>
  23. map[string][]string{
  24. " ": {"I"},
  25. " I": {"am"},
  26. "I am": {"a", "not"},
  27. "a free": {"man!"},
  28. "am a": {"free"},
  29. "am not": {"a"},
  30. "a number!": {"I"},
  31. "number! I": {"am"},
  32. "not a": {"number!"},
  33. }</pre>
  34. While each prefix consists of multiple words, we
  35. store prefixes in the map as a single <code>string</code>.
  36. It would seem more natural to store the prefix as a
  37. <code>[]string</code>, but we can't do this with a map because the
  38. key type of a map must implement equality (and slices do not).
  39. <br/><br/>
  40. Therefore, in most of our code we will model prefixes as a
  41. <code>[]string</code> and join the strings together with a space
  42. to generate the map key:
  43. <pre>
  44. Prefix Map key
  45. []string{"", ""} " "
  46. []string{"", "I"} " I"
  47. []string{"I", "am"} "I am"
  48. </pre>
  49. </step>
  50. <step title="The Chain struct" src="doc/codewalk/markov.go:/type Chain/,/}/">
  51. The complete state of the chain table consists of the table itself and
  52. the word length of the prefixes. The <code>Chain</code> struct stores
  53. this data.
  54. </step>
  55. <step title="The NewChain constructor function" src="doc/codewalk/markov.go:/func New/,/\n}/">
  56. The <code>Chain</code> struct has two unexported fields (those that
  57. do not begin with an upper case character), and so we write a
  58. <code>NewChain</code> constructor function that initializes the
  59. <code>chain</code> map with <code>make</code> and sets the
  60. <code>prefixLen</code> field.
  61. <br/><br/>
  62. This is constructor function is not strictly necessary as this entire
  63. program is within a single package (<code>main</code>) and therefore
  64. there is little practical difference between exported and unexported
  65. fields. We could just as easily write out the contents of this function
  66. when we want to construct a new Chain.
  67. But using these unexported fields is good practice; it clearly denotes
  68. that only methods of Chain and its constructor function should access
  69. those fields. Also, structuring <code>Chain</code> like this means we
  70. could easily move it into its own package at some later date.
  71. </step>
  72. <step title="The Prefix type" src="doc/codewalk/markov.go:/type Prefix/">
  73. Since we'll be working with prefixes often, we define a
  74. <code>Prefix</code> type with the concrete type <code>[]string</code>.
  75. Defining a named type clearly allows us to be explicit when we are
  76. working with a prefix instead of just a <code>[]string</code>.
  77. Also, in Go we can define methods on any named type (not just structs),
  78. so we can add methods that operate on <code>Prefix</code> if we need to.
  79. </step>
  80. <step title="The String method" src="doc/codewalk/markov.go:/func[^\n]+String/,/}/">
  81. The first method we define on <code>Prefix</code> is
  82. <code>String</code>. It returns a <code>string</code> representation
  83. of a <code>Prefix</code> by joining the slice elements together with
  84. spaces. We will use this method to generate keys when working with
  85. the chain map.
  86. </step>
  87. <step title="Building the chain" src="doc/codewalk/markov.go:/func[^\n]+Build/,/\n}/">
  88. The <code>Build</code> method reads text from an <code>io.Reader</code>
  89. and parses it into prefixes and suffixes that are stored in the
  90. <code>Chain</code>.
  91. <br/><br/>
  92. The <code><a href="/pkg/io/#Reader">io.Reader</a></code> is an
  93. interface type that is widely used by the standard library and
  94. other Go code. Our code uses the
  95. <code><a href="/pkg/fmt/#Fscan">fmt.Fscan</a></code> function, which
  96. reads space-separated values from an <code>io.Reader</code>.
  97. <br/><br/>
  98. The <code>Build</code> method returns once the <code>Reader</code>'s
  99. <code>Read</code> method returns <code>io.EOF</code> (end of file)
  100. or some other read error occurs.
  101. </step>
  102. <step title="Buffering the input" src="doc/codewalk/markov.go:/bufio\.NewReader/">
  103. This function does many small reads, which can be inefficient for some
  104. <code>Readers</code>. For efficiency we wrap the provided
  105. <code>io.Reader</code> with
  106. <code><a href="/pkg/bufio/">bufio.NewReader</a></code> to create a
  107. new <code>io.Reader</code> that provides buffering.
  108. </step>
  109. <step title="The Prefix variable" src="doc/codewalk/markov.go:/make\(Prefix/">
  110. At the top of the function we make a <code>Prefix</code> slice
  111. <code>p</code> using the <code>Chain</code>'s <code>prefixLen</code>
  112. field as its length.
  113. We'll use this variable to hold the current prefix and mutate it with
  114. each new word we encounter.
  115. </step>
  116. <step title="Scanning words" src="doc/codewalk/markov.go:/var s string/,/\n }/">
  117. In our loop we read words from the <code>Reader</code> into a
  118. <code>string</code> variable <code>s</code> using
  119. <code>fmt.Fscan</code>. Since <code>Fscan</code> uses space to
  120. separate each input value, each call will yield just one word
  121. (including punctuation), which is exactly what we need.
  122. <br/><br/>
  123. <code>Fscan</code> returns an error if it encounters a read error
  124. (<code>io.EOF</code>, for example) or if it can't scan the requested
  125. value (in our case, a single string). In either case we just want to
  126. stop scanning, so we <code>break</code> out of the loop.
  127. </step>
  128. <step title="Adding a prefix and suffix to the chain" src="doc/codewalk/markov.go:/ key/,/key\], s\)">
  129. The word stored in <code>s</code> is a new suffix. We add the new
  130. prefix/suffix combination to the <code>chain</code> map by computing
  131. the map key with <code>p.String</code> and appending the suffix
  132. to the slice stored under that key.
  133. <br/><br/>
  134. The built-in <code>append</code> function appends elements to a slice
  135. and allocates new storage when necessary. When the provided slice is
  136. <code>nil</code>, <code>append</code> allocates a new slice.
  137. This behavior conveniently ties in with the semantics of our map:
  138. retrieving an unset key returns the zero value of the value type and
  139. the zero value of <code>[]string</code> is <code>nil</code>.
  140. When our program encounters a new prefix (yielding a <code>nil</code>
  141. value in the map) <code>append</code> will allocate a new slice.
  142. <br/><br/>
  143. For more information about the <code>append</code> function and slices
  144. in general see the
  145. <a href="/doc/articles/slices_usage_and_internals.html">Slices: usage and internals</a> article.
  146. </step>
  147. <step title="Pushing the suffix onto the prefix" src="doc/codewalk/markov.go:/p\.Shift/">
  148. Before reading the next word our algorithm requires us to drop the
  149. first word from the prefix and push the current suffix onto the prefix.
  150. <br/><br/>
  151. When in this state
  152. <pre>
  153. p == Prefix{"I", "am"}
  154. s == "not" </pre>
  155. the new value for <code>p</code> would be
  156. <pre>
  157. p == Prefix{"am", "not"}</pre>
  158. This operation is also required during text generation so we put
  159. the code to perform this mutation of the slice inside a method on
  160. <code>Prefix</code> named <code>Shift</code>.
  161. </step>
  162. <step title="The Shift method" src="doc/codewalk/markov.go:/func[^\n]+Shift/,/\n}/">
  163. The <code>Shift</code> method uses the built-in <code>copy</code>
  164. function to copy the last len(p)-1 elements of <code>p</code> to
  165. the start of the slice, effectively moving the elements
  166. one index to the left (if you consider zero as the leftmost index).
  167. <pre>
  168. p := Prefix{"I", "am"}
  169. copy(p, p[1:])
  170. // p == Prefix{"am", "am"}</pre>
  171. We then assign the provided <code>word</code> to the last index
  172. of the slice:
  173. <pre>
  174. // suffix == "not"
  175. p[len(p)-1] = suffix
  176. // p == Prefix{"am", "not"}</pre>
  177. </step>
  178. <step title="Generating text" src="doc/codewalk/markov.go:/func[^\n]+Generate/,/\n}/">
  179. The <code>Generate</code> method is similar to <code>Build</code>
  180. except that instead of reading words from a <code>Reader</code>
  181. and storing them in a map, it reads words from the map and
  182. appends them to a slice (<code>words</code>).
  183. <br/><br/>
  184. <code>Generate</code> uses a conditional for loop to generate
  185. up to <code>n</code> words.
  186. </step>
  187. <step title="Getting potential suffixes" src="doc/codewalk/markov.go:/choices/,/}\n/">
  188. At each iteration of the loop we retrieve a list of potential suffixes
  189. for the current prefix. We access the <code>chain</code> map at key
  190. <code>p.String()</code> and assign its contents to <code>choices</code>.
  191. <br/><br/>
  192. If <code>len(choices)</code> is zero we break out of the loop as there
  193. are no potential suffixes for that prefix.
  194. This test also works if the key isn't present in the map at all:
  195. in that case, <code>choices</code> will be <code>nil</code> and the
  196. length of a <code>nil</code> slice is zero.
  197. </step>
  198. <step title="Choosing a suffix at random" src="doc/codewalk/markov.go:/next := choices/,/Shift/">
  199. To choose a suffix we use the
  200. <code><a href="/pkg/math/rand/#Intn">rand.Intn</a></code> function.
  201. It returns a random integer up to (but not including) the provided
  202. value. Passing in <code>len(choices)</code> gives us a random index
  203. into the full length of the list.
  204. <br/><br/>
  205. We use that index to pick our new suffix, assign it to
  206. <code>next</code> and append it to the <code>words</code> slice.
  207. <br/><br/>
  208. Next, we <code>Shift</code> the new suffix onto the prefix just as
  209. we did in the <code>Build</code> method.
  210. </step>
  211. <step title="Returning the generated text" src="doc/codewalk/markov.go:/Join\(words/">
  212. Before returning the generated text as a string, we use the
  213. <code>strings.Join</code> function to join the elements of
  214. the <code>words</code> slice together, separated by spaces.
  215. </step>
  216. <step title="Command-line flags" src="doc/codewalk/markov.go:/Register command-line flags/,/prefixLen/">
  217. To make it easy to tweak the prefix and generated text lengths we
  218. use the <code><a href="/pkg/flag/">flag</a></code> package to parse
  219. command-line flags.
  220. <br/><br/>
  221. These calls to <code>flag.Int</code> register new flags with the
  222. <code>flag</code> package. The arguments to <code>Int</code> are the
  223. flag name, its default value, and a description. The <code>Int</code>
  224. function returns a pointer to an integer that will contain the
  225. user-supplied value (or the default value if the flag was omitted on
  226. the command-line).
  227. </step>
  228. <step title="Program set up" src="doc/codewalk/markov.go:/flag.Parse/,/rand.Seed/">
  229. The <code>main</code> function begins by parsing the command-line
  230. flags with <code>flag.Parse</code> and seeding the <code>rand</code>
  231. package's random number generator with the current time.
  232. <br/><br/>
  233. If the command-line flags provided by the user are invalid the
  234. <code>flag.Parse</code> function will print an informative usage
  235. message and terminate the program.
  236. </step>
  237. <step title="Creating and building a new Chain" src="doc/codewalk/markov.go:/c := NewChain/,/c\.Build/">
  238. To create the new <code>Chain</code> we call <code>NewChain</code>
  239. with the value of the <code>prefix</code> flag.
  240. <br/><br/>
  241. To build the chain we call <code>Build</code> with
  242. <code>os.Stdin</code> (which implements <code>io.Reader</code>) so
  243. that it will read its input from standard input.
  244. </step>
  245. <step title="Generating and printing text" src="doc/codewalk/markov.go:/c\.Generate/,/fmt.Println/">
  246. Finally, to generate text we call <code>Generate</code> with
  247. the value of the <code>words</code> flag and assigning the result
  248. to the variable <code>text</code>.
  249. <br/><br/>
  250. Then we call <code>fmt.Println</code> to write the text to standard
  251. output, followed by a carriage return.
  252. </step>
  253. <step title="Using this program" src="doc/codewalk/markov.go">
  254. To use this program, first build it with the
  255. <a href="/cmd/go/">go</a> command:
  256. <pre>
  257. $ go build markov.go</pre>
  258. And then execute it while piping in some input text:
  259. <pre>
  260. $ echo "a man a plan a canal panama" \
  261. | ./markov -prefix=1
  262. a plan a man a plan a canal panama</pre>
  263. Here's a transcript of generating some text using the Go distribution's
  264. README file as source material:
  265. <pre>
  266. $ ./markov -words=10 &lt; $GOROOT/README
  267. This is the source code repository for the Go source
  268. $ ./markov -prefix=1 -words=10 &lt; $GOROOT/README
  269. This is the go directory (the one containing this README).
  270. $ ./markov -prefix=1 -words=10 &lt; $GOROOT/README
  271. This is the variable if you have just untarred a</pre>
  272. </step>
  273. <step title="An exercise for the reader" src="doc/codewalk/markov.go">
  274. The <code>Generate</code> function does a lot of allocations when it
  275. builds the <code>words</code> slice. As an exercise, modify it to
  276. take an <code>io.Writer</code> to which it incrementally writes the
  277. generated text with <code>Fprint</code>.
  278. Aside from being more efficient this makes <code>Generate</code>
  279. more symmetrical to <code>Build</code>.
  280. </step>
  281. </codewalk>