test-foof-loop.scm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. ;;; -*- Mode: Scheme -*-
  2. ;;;; Extensible Looping Macros
  3. ;;;; Test Suite
  4. ;;; This code is written by Taylor R. Campbell and placed in the Public
  5. ;;; Domain. All warranties are disclaimed.
  6. (define open-input-string open-string-input-port)
  7. (define (identity-procedure x) x)
  8. (define-test-suite loop-tests
  9. "Taylor R. Campbell's and Alex Shinn's loop macros")
  10. (define-test-suite (loop-tests.null-loop loop-tests)
  11. "Loops that do nothing")
  12. (define-test-case loop-tests.null-loop no-recursive-call ()
  13. (test-eqv 0 (loop continue () 0)))
  14. (define-test-case loop-tests.null-loop loop-until-true ()
  15. (test-eqv 0 (loop ((until #t)) => 0)))
  16. (define-test-suite (loop-tests.trivial-do loop-tests)
  17. "Loops trivially translated from DO")
  18. (define-test-case loop-tests.trivial-do iota ()
  19. (test-equal '(0 1 2)
  20. (loop continue ((integer 0 (+ integer 1))
  21. (list '() (cons integer list)))
  22. (if (= integer 3)
  23. (reverse list)
  24. (continue)))))
  25. (define-test-case loop-tests.trivial-do list-sum ()
  26. (test-eqv 25
  27. ;; R5RS, 4.2.4, p12, translated from DO.
  28. (let ((x '(1 3 5 7 9)))
  29. (loop ((x x (cdr x))
  30. (sum 0 (+ sum (car x)))
  31. (until (null? x)))
  32. => sum))))
  33. (define-test-suite (loop-tests.trivial-named-let loop-tests)
  34. "Loops trivially translated from named LET")
  35. (define-test-case loop-tests.trivial-named-let successive-sum ()
  36. (test-eqv 45
  37. (loop continue ((i 0) (sum 0))
  38. (if (= i 10)
  39. sum
  40. (continue (+ i 1) (+ sum i))))))
  41. (define-test-case loop-tests.trivial-named-let partition-list-by-sign ()
  42. (test-equal '((6 1 3) (-5 -2))
  43. ;; R5RS, 4.2.4, p12, translated from named LET.
  44. (loop continue ((numbers '(3 -2 1 6 -5))
  45. (nonneg '())
  46. (neg '()))
  47. (cond ((null? numbers) (list nonneg neg))
  48. ((>= (car numbers) 0)
  49. (continue (cdr numbers)
  50. (cons (car numbers) nonneg)
  51. neg))
  52. ((< (car numbers) 0)
  53. (continue (cdr numbers)
  54. nonneg
  55. (cons (car numbers) neg)))))))
  56. (define-test-suite (loop-tests.in-list loop-tests)
  57. "IN-LIST iterator")
  58. (define-test-case loop-tests.in-list sum ()
  59. (test-eqv 6
  60. (loop ((for element (in-list '(1 2 3)))
  61. (with sum 0 (+ sum element)))
  62. => sum)))
  63. (define-test-case loop-tests.in-list reverse ()
  64. (test-equal '(2 1 0)
  65. (loop ((for element (in-list '(0 1 2)))
  66. (with reversed '() (cons element reversed)))
  67. => reversed)))
  68. (define-test-case loop-tests.in-list find-matching-items ()
  69. (test-equal '(-4 #F FOO)
  70. (let ((items '(3 -1.2 -4 1 FOO 9 2 6 FROTZ)))
  71. (define (find-matching-item list predicate)
  72. (loop continue ((for item (in-list list)))
  73. => #f
  74. (if (predicate item) item (continue))))
  75. (list (find-matching-item items
  76. (lambda (item) (and (integer? item) (negative? item))))
  77. (find-matching-item items pair?)
  78. (find-matching-item items symbol?)))))
  79. (define-test-case loop-tests.in-list pairwise-sum ()
  80. (test-equal '(5 7 9)
  81. (loop ((for a (in-list '(1 2 3)))
  82. (for b (in-list '(4 5 6)))
  83. (with pairwise-sum '() (cons (+ a b) pairwise-sum)))
  84. => (reverse pairwise-sum))))
  85. (define-test-case loop-tests.in-list plist->alist ()
  86. (test-equal '((:X FOO) (:Y BAR) (:Z BAZ))
  87. (loop ((for key tail (in-list '(:X FOO :Y BAR :Z BAZ) cddr))
  88. (with alist '() (cons (list key (cadr tail)) alist)))
  89. => (reverse alist))))
  90. (define-test-case loop-tests.in-list partition ()
  91. (test-equal '((4 2 6) (3 1 1 5 9 5))
  92. (loop continue ((for element (in-list '(3 1 4 1 5 9 2 6 5)))
  93. (with even '())
  94. (with odd '()))
  95. => (list (reverse even) (reverse odd))
  96. (if (even? element)
  97. (continue (=> even (cons element even)))
  98. (continue (=> odd (cons element odd)))))))
  99. (define-test-case loop-tests.in-list inner-product ()
  100. (test-equal 32
  101. (loop ((for components (in-lists '((1 2 3) (4 5 6))))
  102. (with inner-product 0
  103. (+ inner-product
  104. (loop ((for component (in-list components))
  105. (with product 1 (* product component)))
  106. => product))))
  107. => inner-product)))
  108. (define-test-case loop-tests.in-list matrix-transposition ()
  109. (test-equal '((C F) (B E) (A D))
  110. (loop ((for columns (in-lists '((A B C) (D E F))))
  111. (with rows '() (cons columns rows)))
  112. => rows)))
  113. (define-test-suite (loop-tests.in-vector loop-tests)
  114. "IN-VECTOR and IN-VECTOR-REVERSE iterator")
  115. (define-test-case loop-tests.in-vector sum ()
  116. (test-eqv 20
  117. (loop ((for element (in-vector '#(2 4 6 8)))
  118. (with sum 0 (+ sum element)))
  119. => sum)))
  120. (define-test-case loop-tests.in-vector vector-suffix->list ()
  121. (test-equal '(4 1 5 9)
  122. (loop ((for element (in-vector '#(3 1 4 1 5 9) 2))
  123. (with list '() (cons element list)))
  124. => (reverse list))))
  125. (define-test-case loop-tests.in-vector subvector->list ()
  126. (test-equal '(1 4 1 5)
  127. (loop ((for element (in-vector-reverse '#(3 1 4 1 5 9) 5 1))
  128. (with list '() (cons element list)))
  129. => list)))
  130. (define-test-case loop-tests.in-vector reverse-subvector->list ()
  131. (test-equal '(5 1 4 1)
  132. (loop ((for element (in-vector '#(3 1 4 1 5 9) 1 5))
  133. (with list '() (cons element list)))
  134. => list)))
  135. (define-test-case loop-tests.in-vector linear-search ()
  136. (test-equal 2
  137. (loop continue ((for element index (in-vector '#(FOO BAR BAZ QUUX))))
  138. (if (eq? element 'BAZ)
  139. index
  140. (continue)))))
  141. (define-test-case loop-tests.in-vector vector-reverse ()
  142. (test-equal '#(E D C)
  143. (let ()
  144. (define (vector-reverse-copy vector start end)
  145. (let* ((length (- end start))
  146. (vector* (make-vector length)))
  147. (loop ((for element index (in-vector vector start end)))
  148. (vector-set! vector* (- (- end 1) index) element))
  149. vector*))
  150. (vector-reverse-copy '#(A B C D E F G H I) 2 5))))
  151. (define-test-case loop-tests.in-vector accelerated-alphabetic-traversal ()
  152. (test-equal '((A 0) (B 1) (D 3) (H 7) (P 15))
  153. ((lambda (body)
  154. (body '#(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)))
  155. (lambda (alphabet-vector)
  156. (loop continue ((for element index (in-vector alphabet-vector))
  157. (with result '()
  158. (cons (list element index) result)))
  159. => (reverse result)
  160. (continue (=> index (+ 1 (* 2 index)))))))))
  161. (define-test-suite (loop-tests.in-string loop-tests)
  162. "IN-STRING and IN-STRING-REVERSE iterators")
  163. (define-test-case loop-tests.in-string linear-search ()
  164. (test-eqv 4
  165. (loop continue ((for char index (in-string "foobarbaz")))
  166. (if (char=? char #\a)
  167. index
  168. (continue)))))
  169. (define-test-case loop-tests.in-string linear-search-reverse ()
  170. (test-eqv 7
  171. (loop continue ((for char index (in-string-reverse "foobarbaz")))
  172. => #f
  173. (if (char=? char #\a)
  174. index
  175. (continue)))))
  176. (define-test-case loop-tests.in-string string->list ()
  177. (test-equal '(#\o #\o #\b #\a)
  178. (loop ((for char (in-string "foobar" 1 5))
  179. (with chars '() (cons char chars)))
  180. => (reverse chars))))
  181. (define-test-suite (loop-tests.io-loops loop-tests)
  182. "IN-PORT and IN-FILE iterators")
  183. (define-test-case loop-tests.io-loops read-chars ()
  184. (test-equal '(#\x #\y #\z)
  185. (loop ((for char (in-port (open-input-string "xyz")))
  186. (with chars '() (cons char chars)))
  187. => (reverse chars))))
  188. (define-test-case loop-tests.io-loops read-expressions ()
  189. (test-equal '(foo bar (baz quux) #(zot))
  190. (loop ((for expression
  191. (in-port (open-input-string "foo bar (baz quux) #(zot)")
  192. read))
  193. (with expressions '() (cons expression expressions)))
  194. => (reverse expressions))))
  195. (define-test-case loop-tests.io-loops read-with-custom-eof ()
  196. (test-equal '(foo bar baz)
  197. (loop ((for term
  198. (in-port (open-input-string "foo bar baz eof quux")
  199. read
  200. (lambda (term)
  201. (if (eof-object? term)
  202. (test-failure "Premature real EOF.")
  203. (eq? term 'eof)))))
  204. (with terms '() (cons term terms)))
  205. => (reverse terms))))
  206. ;++ This should test IN-FILE, but we need some sort of temporary file
  207. ;++ generation utility, and to implement setup & teardown actions.
  208. (define-test-suite (loop-tests.integer-intervals loop-tests)
  209. "UP-FROM and DOWN-FROM iterators")
  210. (define-test-case loop-tests.integer-intervals successive-sum ()
  211. (test-equal 45
  212. (loop ((for i (up-from 0 (to 10)))
  213. (with sum 0 (+ sum i)))
  214. => sum)))
  215. (define-test-case loop-tests.integer-intervals reverse-iota-evens ()
  216. (test-equal '(8 6 4 2 0)
  217. (loop ((for i (up-from 0 (to 10) (by 2)))
  218. (with list '() (cons i list)))
  219. => list)))
  220. (define-test-case loop-tests.integer-intervals iota-odds ()
  221. (test-equal '(1 3 5 7 9)
  222. (loop ((for i (down-from 11 (to 1) (by 2)))
  223. (with list '() (cons i list)))
  224. => list)))
  225. (define-test-case loop-tests.integer-intervals list-of-squares ()
  226. (test-equal '(0 1 4 9 16)
  227. (loop ((for i (down-from 5 (to 0)))
  228. (with squares '() (cons (* i i) squares)))
  229. => squares)))
  230. (define-test-case loop-tests.integer-intervals sieve-of-eratosthenes ()
  231. (test-equal '(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59)
  232. (let ()
  233. (define (make-bit-string size set?)
  234. (make-string size (if set? #\1 #\0)))
  235. (define (bit-string-set! bit-string index)
  236. (string-set! bit-string index #\1))
  237. (define (bit-string-clear! bit-string index)
  238. (string-set! bit-string index #\0))
  239. (define (bit-string-set? bit-string index)
  240. (char=? #\1 (string-ref bit-string index)))
  241. (define (sieve n)
  242. (let ((prime-table (make-bit-string (- n 2) #t)))
  243. (define (prime? k) (bit-string-set? prime-table (- k 2)))
  244. (define (not-prime! k)
  245. (bit-string-clear! prime-table (- k 2)))
  246. (define (prime! k)
  247. (loop ((for i (up-from (* k k) (to n) (by k))))
  248. (not-prime! i)))
  249. (loop ((for k (up-from 2 (to n)))
  250. (with prime-list '()
  251. (if (prime? k)
  252. (begin (prime! k) (cons k prime-list))
  253. prime-list)))
  254. => (reverse prime-list))))
  255. (sieve 60))))
  256. (define-test-suite (loop-tests.accumulation loop-tests)
  257. "Accumulation iterators")
  258. (define-test-case loop-tests.accumulation iota ()
  259. (test-equal '(0 1 2 3 4 5 6 7 8 9)
  260. (loop ((for i (up-from 0 (to 10)))
  261. (for list (listing i)))
  262. => list)))
  263. (define-test-case loop-tests.accumulation append ()
  264. (test-equal '(a b c d e f)
  265. (let ()
  266. (define (append list tail)
  267. (loop ((for element (in-list list))
  268. (for tail (listing (initial tail) element)))
  269. => tail))
  270. (append '(a b c) '(d e f)))))
  271. (define-test-case loop-tests.accumulation append-reverse ()
  272. (test-equal '(f e d c b a)
  273. (let ()
  274. (define (append-reverse list tail)
  275. (loop ((for element (in-list list))
  276. (for tail (listing-reverse (initial tail) element)))
  277. => tail))
  278. (append-reverse '(d e f) '(c b a)))))
  279. (define-test-case loop-tests.accumulation iota-reverse ()
  280. (test-equal '(9 8 7 6 5 4 3 2 1 0)
  281. (loop ((for i (up-from 0 (to 10)))
  282. (for list (listing-reverse i)))
  283. => list)))
  284. (define-test-case loop-tests.accumulation non-reentrant-map ()
  285. (test-equal '(1 4 9 16 25)
  286. (loop ((for i (in-list '(1 2 3 4 5)))
  287. (for squares (listing! (* i i))))
  288. => squares)))
  289. (define-test-case loop-tests.accumulation even-product-iota! ()
  290. (test-equal '(INITIAL 0 4 16 36 64)
  291. (let ((x (cons 'INITIAL '())))
  292. (loop ((for i (up-from 0 (to 10)))
  293. (for result (listing-into! x (* i i) (if (even? i))))))
  294. x)))
  295. (define-test-case loop-tests.accumulation concatenate ()
  296. (test-equal '(A B C P Q R 0 1 2)
  297. (loop ((for list (in-list '((A B C) (P Q R) (0 1 2))))
  298. (for result (appending list)))
  299. => result)))
  300. (define-test-case loop-tests.accumulation reverse-concatenate ()
  301. (test-equal '(2 1 0 R Q P C B A)
  302. (loop ((for list (in-list '((A B C) (P Q R) (0 1 2))))
  303. (for result (appending-reverse list)))
  304. => result)))
  305. (define-test-case loop-tests.accumulation maximize-if-even ()
  306. (test-equal 6
  307. (loop ((for i (in-list '(3 1 4 1 5 9 2 6 5 3 5)))
  308. (for j (maximizing i (if (even? i)))))
  309. => j)))
  310. (define-test-case loop-tests.accumulation minimize-if-odd ()
  311. (test-equal 1
  312. (loop ((for i (in-list '(3 1 4 1 5 9 2 6 5 3 5)))
  313. (for j (minimizing i (if (odd? i)))))
  314. => j)))
  315. (define-test-case loop-tests.accumulation sum-of-squares-of-valid-numbers ()
  316. (test-equal 1300
  317. (loop ((for string (in-list '("a" "12" "x" "34")))
  318. (for sum (summing (string->number string)
  319. => (lambda (number) (* number number)))))
  320. => sum)))
  321. (define-test-case loop-tests.accumulation sum-of-valid-even-numbers ()
  322. (test-equal 24
  323. (loop ((for string (in-list '("a" "2" "3" "6" "b" "16" "17" "x" "19")))
  324. (for sum (summing (values (string->number string))
  325. (lambda (x) (and x (even? x)))
  326. => (lambda (number) number))))
  327. => sum)))
  328. (define-test-case loop-tests.accumulation factorial ()
  329. (test-equal 720
  330. (loop ((for i (up-from 1 (to (+ 6 1))))
  331. (for factorial (multiplying i)))
  332. => factorial)))
  333. (define-test-suite (loop-tests.misc loop-tests)
  334. "Miscellaneous loops")
  335. (define-test-case loop-tests.misc obfuscated-loop-invocation ()
  336. (test-equal '((0 () i (i j k p q r))
  337. (1 (0) k (k p q r))
  338. (2 (1 0) q (q r)))
  339. (loop continue ((with a 0)
  340. (with b '() (cons a b))
  341. (for c d (in-list '(i j k p q r)))
  342. (for result (listing (list a b c d))))
  343. => result
  344. (continue (+ a 1)
  345. (=> d (cddr d))))))
  346. (define-test-case loop-tests.misc vector-quick-sort ()
  347. (let ()
  348. (define (vector-copy vector)
  349. (let* ((length (vector-length vector))
  350. (vector* (make-vector length)))
  351. (loop ((for element index (in-vector vector)))
  352. (vector-set! vector* index element))
  353. vector*))
  354. (loop ((for vector (in-list '(#(A B C 8 6 5 3 1 4 0 7 2 9 D E F)
  355. #(A B C 2 7 4 9 3 6 8 5 0 1 D E F)
  356. #(A B C 0 8 9 3 5 4 6 1 7 2 D E F)
  357. #(A B C 7 8 3 0 2 1 4 6 9 5 D E F)
  358. #(A B C 9 7 4 8 3 0 1 2 5 6 D E F)
  359. #(A B C 1 9 2 6 4 7 3 8 0 5 D E F)))))
  360. (let ((vector (vector-copy vector)))
  361. (vector-quick-sort! vector 3 (- (vector-length vector) 3)
  362. identity-procedure
  363. <)
  364. (if (not (equal? vector '#(A B C 0 1 2 3 4 5 6 7 8 9 D E F)))
  365. (test-failure "Vector quick-sort yielded an unsorted vector:"
  366. vector))))))
  367. (define (vector-quick-sort! vector start end key-selector key<)
  368. (define (select-pivot vector start end)
  369. (vector-ref vector (quotient (+ start end) 2)))
  370. (loop sort ((start start) (end end))
  371. (if (< 1 (- end start))
  372. (let ((pivot (key-selector (select-pivot vector start end))))
  373. (loop continue ((i start) (j end))
  374. (let ((i (loop scan ((for i (up-from i)))
  375. (if (key< (key-selector (vector-ref vector i)) pivot)
  376. (scan)
  377. i)))
  378. (j (loop scan ((for j (down-from j)))
  379. (if (key< pivot (key-selector (vector-ref vector j)))
  380. (scan)
  381. j))))
  382. (if (< i j)
  383. (begin (vector-exchange! vector i j)
  384. (continue (+ i 1) j))
  385. (begin (sort (=> end i))
  386. (sort (=> start (+ j 1)))))))))))
  387. (define (vector-exchange! vector i j)
  388. (let ((vi (vector-ref vector i))
  389. (vj (vector-ref vector j)))
  390. (vector-set! vector j vi)
  391. (vector-set! vector i vj)))
  392. (run-test-suite loop-tests)