02-code.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. (import
  2. (except (rnrs base) let-values map error)
  3. (only (guile)
  4. lambda* λ
  5. current-output-port)
  6. (srfi srfi-1))
  7. ;; =======
  8. ;; CHANGES
  9. ;; =======
  10. ;; This section lists differences to the book's code.
  11. ;; Some procedures have been renamed for better readability and compatibility
  12. ;; reasons:
  13. ;; elt -> element
  14. ;; random-elt -> random-element
  15. ;; car -> car/nil
  16. ;; mappend -> map-append
  17. ;; ===============================
  18. ;; Common Lisp compatibility layer
  19. ;; ===============================
  20. (define NIL '())
  21. (define element list-ref)
  22. (define car/nil
  23. (λ (possibly-empty-list)
  24. "In Common Lisp car of an empty list returns the empty list
  25. called NIL. In Scheme it would be an error to call car on an
  26. empty list. We write a wrapper, to avoid an error."
  27. (cond
  28. [(null? possibly-empty-list) NIL]
  29. [else (car possibly-empty-list)])))
  30. (define rest
  31. (λ (lst)
  32. (cond
  33. [(null? lst) NIL]
  34. [else (cdr lst)])))
  35. (define assoc
  36. (λ (key alist)
  37. (let ([res (assoc key alist)])
  38. (cond
  39. [res res]
  40. [else NIL]))))
  41. ;; =================
  42. ;; HELPER PROCEDURES
  43. ;; =================
  44. (define displayln
  45. (lambda* (#:key (output-port (current-output-port)) . msgs)
  46. (display (string-append
  47. (string-join
  48. (map (λ (msg) (simple-format #f "~s" msg)) msgs)
  49. " ") "\n")
  50. output-port)))
  51. ;; ======================
  52. ;; from previous chapters
  53. ;; ======================
  54. (define map-append
  55. (λ (proc lst)
  56. (apply append (map proc lst))))
  57. ;; ==============================
  58. ;; 2-02-straight-forward-solution
  59. ;; ==============================
  60. ;; helper functions
  61. (define random-element
  62. (λ (choices)
  63. (element choices (random (length choices)))))
  64. (define one-of
  65. (λ (set)
  66. "Choose one element of a given set of elements and return
  67. it. one-of returns a list, so that the result can be
  68. appended to any other result."
  69. (list (random-element set))))
  70. ;; define the over all sentence structure
  71. (define sentence
  72. (λ ()
  73. (append (noun-phrase)
  74. (verb-phrase))))
  75. (define noun-phrase
  76. (λ ()
  77. (append (Article)
  78. (Noun))))
  79. (define verb-phrase
  80. (λ ()
  81. (append (Verb)
  82. (noun-phrase))))
  83. ;; terminal symbols
  84. (define Article (λ () (one-of '(the a))))
  85. (define Noun (λ () (one-of '(man ball woman table))))
  86. (define Verb (λ () (one-of '(hit took saw liked))))
  87. ;; ========================
  88. ;; 2-03-rule-based-solution
  89. ;; ========================
  90. ;; define parameters, global constants, *name* naming-convention in scheme
  91. (define *simple-grammar*
  92. '((sentence -> (noun-phrase verb-phrase))
  93. (noun-phrase -> (Article Noun))
  94. (verb-phrase -> (Verb noun-phrase))
  95. (Article -> the a)
  96. (Noun -> man ball woman table)
  97. (Verb -> hit took saw liked))
  98. #|A grammar for a trivial subset of English.|#)
  99. (define *grammar* *simple-grammar* #|The grammar used by
  100. generate. Initially, this is *simple-grammar*, but we can
  101. switch to other grammars.|#)
  102. ;; define a data abstraction layer for accessing rules
  103. (define rule-lhs
  104. (λ (rule)
  105. "The left-hand side of a rule."
  106. (first rule)))
  107. (define rule-rhs
  108. (λ (rule)
  109. "The right-hand side of a rule."
  110. (rest (rest rule))))
  111. (define rewrites
  112. (λ (grammar category)
  113. "Return a list of the possible rewrites for this category."
  114. ;; (displayln "in rewrites")
  115. ;; (displayln "getting RHS of" category "from" grammar)
  116. ;; (displayln (assoc category grammar))
  117. (rule-rhs (assoc category grammar))))
  118. ;; write the generate procedure
  119. (define generate
  120. (λ (grammar phrase)
  121. (cond
  122. [(pair? phrase)
  123. ;; If we get a list of things, as for a previously
  124. ;; rewritten expression, we apply generate to all
  125. ;; parts of the list and concattenate the result into
  126. ;; one list.
  127. (map-append (λ (part) (generate grammar part))
  128. phrase)]
  129. [else
  130. ;; If we get a symbol, terminal or non-terminal, we
  131. ;; try to get its choices for substitution.
  132. (let ([choices (rewrites grammar phrase)])
  133. (cond
  134. [(null? choices)
  135. ;; If there are no choices, we are dealing with a
  136. ;; terminal symbol. If that is the case, we wrap
  137. ;; the symbol into a list to be able to append it
  138. ;; to other previous substitutions.
  139. (list phrase)]
  140. [else
  141. ;; If there are choices, we are dealing with a
  142. ;; non-terminal symbol. We make a choice by
  143. ;; choosing a random element.
  144. (generate grammar (random-element choices))]))])))
  145. ;; =========
  146. ;; EXERCISES
  147. ;; =========
  148. ;; EXERCISE 2.1
  149. ;; Write a version of generate that uses cond but avoids
  150. ;; calling rewrites twice. Already done, see code above.
  151. ;; EXERCISE 2.2
  152. ;; Write a version of generate that explicitly
  153. ;; differentiates between terminal symbols (those with no
  154. ;; rewrite rules) and nonterminal symbols.
  155. (define terminal?
  156. (λ (grammar phrase)
  157. (null? (rewrites grammar phrase))))
  158. (define non-terminal?
  159. (λ (grammar phrase)
  160. (not terminal?)))
  161. (define generate-explicit
  162. (λ (grammar phrase)
  163. "This generate procedure uses explicit predicates, but is
  164. less performant, because the check for terminal symbol
  165. requires looking the symbol up in the grammar, but does not
  166. return the choices, if there are any and thus a second
  167. lookup is potentially required later."
  168. (cond
  169. ;; could be a list of symbols
  170. [(pair? phrase)
  171. (map-append (λ (part) (generate-explicit grammar part))
  172. phrase)]
  173. [(terminal? grammar phrase) (list phrase)]
  174. [else ; non-terminal symbol
  175. (let ([choices (rewrites grammar phrase)])
  176. (generate-explicit grammar (random-element choices)))])))
  177. ;; Now we define a bigger grammar, to show, how the program
  178. ;; can be modified and extended, without changing the
  179. ;; generate procedure.
  180. (define *bigger-grammar*
  181. '((sentence -> (noun-phrase verb-phrase))
  182. (noun-phrase -> (Article Adjective* Noun PersonalPronoun*) (Name) (Pronoun))
  183. (verb-phrase -> (Verb noun-phrase PersonalPronoun*))
  184. (PersonalPronoun* -> () (PersonalPronoun PersonalPronoun*))
  185. (PersonalPronoun -> (Prep noun-phrase))
  186. (Adjective* -> () (Adjective Adjective*))
  187. (Adjective -> big little blue green adiabatic)
  188. (Prep -> to in by with on)
  189. (Article -> the a)
  190. (Name -> Pat Kim Lee Terry Robin)
  191. (Noun -> man ball woman table)
  192. (Verb -> hit took saw liked)
  193. (Pronoun -> he she it these those that)))
  194. ;; We can recognize implicit rules of how to read this
  195. ;; grammar from looking at the generate procedure:
  196. ;; 1. () is the epsilon or empty symbol. Appended to an
  197. ;; unfinished (improper) list, it ends a list.
  198. ;; 2. When symbols are wrapped in parentheses, generate will
  199. ;; map itself to all symbols in the parentheses. This is
  200. ;; equivalent to saying, that a non-terminal symbol will be
  201. ;; substituted with multiple other symbols. Basically it is
  202. ;; AND, not an exclusive OR.
  203. ;; 3. When symbols are not wrapped in parentheses, one of
  204. ;; them is chosen. This is equivalent to making an exclusive
  205. ;; OR choice.
  206. ;; We can use this grammar right away with the generate
  207. ;; procedure.
  208. ;; Lets write a procedure, which takes the result of
  209. ;; generate and writes it as a string.
  210. (define flatten-transform
  211. (λ (lst-or-atom transformation)
  212. "Flatten an arbitrarily nested list and map a transformation
  213. recursively at the same time."
  214. (cond
  215. [(null? lst-or-atom) '()]
  216. [(pair? lst-or-atom)
  217. (append (flatten-transform (car lst-or-atom) transformation)
  218. (flatten-transform (cdr lst-or-atom) transformation))]
  219. [else
  220. (list (transformation lst-or-atom))])))
  221. (define to-string
  222. (λ (parts)
  223. (with-output-to-string
  224. (λ ()
  225. (display
  226. (string-append
  227. (string-join (flatten-transform parts symbol->string) " ")
  228. "."))))))
  229. ;; And for convenience define a procedure to output a random
  230. ;; phrase.
  231. (define display-sentence
  232. (λ ()
  233. (displayln
  234. (to-string
  235. (generate *bigger-grammar* 'sentence)))))
  236. ;; Next we write a procedure, which does not only give us
  237. ;; the terminal symbols, but also the non-terminal symbols,
  238. ;; which lead to the terminal symbols being chosen in a tree
  239. ;; structure. What sounds challenging at first becomes a
  240. ;; small modification of the generate procedure.
  241. (define generate-tree
  242. (λ (grammar phrase)
  243. (cond
  244. [(pair? phrase)
  245. ;; instead of map-append, we use map (or mapcar in common lisp).
  246. (map (λ (part) (generate-tree grammar part))
  247. phrase)]
  248. [else
  249. (let ([choices (rewrites grammar phrase)])
  250. (cond
  251. [(null? choices)
  252. (list phrase)]
  253. [else
  254. ;; we cons the non-terminal to the non-terminal or
  255. ;; terminal that is produced from it, so that we
  256. ;; have it in the result.
  257. (cons phrase
  258. (generate-tree grammar (random-element choices)))]))])))
  259. ;; Next we write a procedure, which generates not only one
  260. ;; phrase from a given symbol, but all possible phrases.
  261. (define no-expansion '())
  262. (define combine-all
  263. (λ (xlist ylist)
  264. (map-append (λ (y)
  265. (map (λ (x) (append x y))
  266. xlist))
  267. ylist)))
  268. (define generate-all
  269. (λ (grammar symb-or-lst)
  270. "Generate a list of all possible expansions of the given
  271. symbol."
  272. (cond
  273. ;; In case we are given an empty list of symbols, we
  274. ;; return the list containing the empty expansion,
  275. ;; which is the empty list itself.
  276. [(null? symb-or-lst) (list no-expansion)]
  277. ;; In case we get a list of symbols to substitute for,
  278. ;; we combine all possibilities of substituting the
  279. ;; first symbol with all possibilities of substituting
  280. ;; the rest of the symbols. This is done using
  281. ;; recursive calls.
  282. [(pair? symb-or-lst)
  283. (combine-all (generate-all grammar (first symb-or-lst))
  284. (generate-all grammar (cdr symb-or-lst)))]
  285. ;; Otherwise ...
  286. [else
  287. (let ([choices (rewrites grammar symb-or-lst)])
  288. (cond
  289. ;; ... if it is a terminal symbol, return a list of
  290. ;; the possible expansions, which are only one, the
  291. ;; terminal itself. Since it is a complete
  292. ;; expansion, we wrap it in a list.
  293. [(null? choices) (list (list symb-or-lst))]
  294. ;; If there are choices for expansions, generate
  295. ;; all expansions for the choices and return the
  296. ;; list of possible expansions.
  297. [else
  298. (map-append (λ (choice) (generate-all grammar choice))
  299. choices)]))])))
  300. ;; Exercise 2.3
  301. ;; Write a trivial grammar for some other language. This can
  302. ;; be a natural language other than English, or perhaps a
  303. ;; subset of a computer language.
  304. (define *chess-phrase-grammar*
  305. '((PHRASE ->
  306. NORMAL-PHRASE
  307. CHECK-PHRASE
  308. OFFER-DRAW-PHRASE
  309. CHECKMATE-PHRASE)
  310. (NORMAL-PHRASE -> (PIECE from FIELD to FIELD))
  311. (CHECK-PHRASE -> (PIECE from FIELD to FIELD check))
  312. (OFFER-DRAW-PHRASE -> (PIECE from FIELD to FIELD draw))
  313. (CHECKMATE-PHRASE -> (PIECE from FIELD to FIELD checkmate))
  314. (PIECE -> pawn knight bishop rook queen king)
  315. (FIELD ->
  316. a1 a2 a3 a4 a5 a6 a7 a8
  317. b1 b2 b3 b4 b5 b6 b7 b8
  318. c1 c2 c3 c4 c5 c6 c7 c8
  319. d1 d2 d3 d4 d5 d6 d7 d8
  320. e1 e2 e3 e4 e5 e6 e7 e8
  321. f1 f2 f3 f4 f5 f6 f7 f8
  322. g1 g2 g3 g4 g5 g6 g7 g8
  323. h1 h2 h3 h4 h5 h6 h7 h8)))
  324. ;; And try it out ...
  325. ;; (displayln
  326. ;; (to-string
  327. ;; (generate *chess-phrase-grammar* 'PHRASE)))
  328. ;; Exercise 2.4
  329. ;; One way of describing combine-all is that it calculates
  330. ;; the cross-product of the function append on the argument
  331. ;; lists. Write the higher-order function cross-product, and
  332. ;; define combine-all in terms of it.
  333. ;; The moral is to make your code as general as possible,
  334. ;; because you never know what you may want to do with it
  335. ;; next.
  336. (define cross-product
  337. (λ (xlist ylist op)
  338. ;; Append all partial results into a long list.
  339. (map-append
  340. ;; Make a list by applying an operation to all elements
  341. ;; of ylist.
  342. (λ (y)
  343. ;; Apply the operation to all pairs of elements from
  344. ;; xlist and ylist.
  345. (map (λ (x) (op x y)) xlist))
  346. ylist)))
  347. (define combine-all-2
  348. (λ (xlist ylist)
  349. (cross-product xlist
  350. ylist
  351. ;; This does not assume lists as inputs,
  352. ;; like append does.
  353. (λ (a b) (cons a (cons b '()))))))
  354. (define generate-all-2
  355. (λ (grammar symb-or-lst)
  356. "Generate a list of all possible expansions of the given
  357. symbol."
  358. (cond
  359. [(null? symb-or-lst) (list no-expansion)]
  360. [(pair? symb-or-lst)
  361. (combine-all (generate-all-2 grammar (first symb-or-lst))
  362. (generate-all-2 grammar (cdr symb-or-lst)))]
  363. [else
  364. (let ([choices (rewrites grammar symb-or-lst)])
  365. (cond
  366. [(null? choices) (list (list symb-or-lst))]
  367. [else
  368. (map-append (λ (choice) (generate-all-2 grammar choice))
  369. choices)]))])))