Skip to content

Commit

Permalink
Test stuff, begin working on solved koans
Browse files Browse the repository at this point in the history
  • Loading branch information
phoe committed May 8, 2020
1 parent 413ae21 commit eab7b89
Show file tree
Hide file tree
Showing 47 changed files with 3,461 additions and 70 deletions.
2 changes: 1 addition & 1 deletion .koans
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@
#:macros
#+quicklisp #:threads
#:extra-credit
)
)
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ in the blank (\_\_\_\_) with appropriate lisp code to make the assert pass.
In order to test code, or evaluate tests interactively, students may copy
and paste code into the lisp command line REPL.

### Testing

To test the koans, execute your lisp interpreter on the file 'contemplate.lisp' e.g.

abcl --noinform --noinit --load test.lisp --eval '(quit)'
ccl -n -l test.lisp -e '(quit)'
clisp -q -norc -ansi test.lisp
ecl -norc -load test.lisp -eval '(quit)'
sbcl --script test.lisp

## Quoting the Ruby Koans instructions

"In test-driven development the mantra has always been, red, green,
Expand Down
70 changes: 70 additions & 0 deletions koans-solved/arrays.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

(define-test basic-array-stuff
;; We make an 8x8 array and then fill it with a checkerboard pattern.
(let ((chess-board (make-array '(8 8))))
;; (DOTIMES (X 8) ...) will iterate with X taking values from 0 to 7.
(dotimes (x 8)
(dotimes (y 8)
;; AREF stands for "array reference".
(setf (aref chess-board x y) (if (evenp (+ x y)) :black :white))))
(assert-true (typep chess-board 'array))
(assert-equal :black (aref chess-board 0 0))
(assert-equal :white (aref chess-board 2 3))
;; The function ARRAY-RANK returns the number of dimensions of the array.
(assert-equal 2 (array-rank chess-board))
;; The function ARRAY-DIMENSIONS returns a list of the cardinality of the
;; array dimensions.
(assert-equal '(8 8) (array-dimensions chess-board))
;; ARRAY-TOTAL-SIZE returns the total number of elements in the array.
(assert-equal 64 (array-total-size chess-board))))

(define-test make-your-own-array
;; Make your own array that satisfies the test.
(let ((color-cube (make-array '(3 3 3))))
;; You may need to modify your array after you create it.
(setf (aref color-cube 0 1 2) :red
(aref color-cube 2 1 0) :white)
(if (typep color-cube '(simple-array T (3 3 3)))
(progn
(assert-equal 3 (array-rank color-cube))
(assert-equal '(3 3 3) (array-dimensions color-cube))
(assert-equal 27 (array-total-size color-cube))
(assert-equal (aref color-cube 0 1 2) :red)
(assert-equal (aref color-cube 2 1 0) :white))
(assert-true nil))))

(define-test adjustable-array
;; The size of an array does not need to be constant.
(let ((x (make-array '(2 2) :initial-element 5 :adjustable t)))
(assert-equal 5 (aref x 1 0))
(assert-equal '(2 2) (array-dimensions x))
(adjust-array x '(3 4))
(assert-equal '(3 4) (array-dimensions x))))

(define-test make-array-from-list
;; One can create arrays with initial contents.
(let ((x (make-array '(4) :initial-contents '(:one :two :three :four))))
(assert-equal '(4) (array-dimensions x))
(assert-equal :one (aref x 0))))

(define-test row-major-index
;; Row major indexing is a way to access elements with a single integer,
;; rather than a list of integers.
(let ((my-array (make-array '(2 2 2 2))))
(dotimes (i (* 2 2 2 2))
(setf (row-major-aref my-array i) i))
(assert-equal 0 (aref my-array 0 0 0 0))
(assert-equal 15 (aref my-array 1 1 1 1))))
65 changes: 65 additions & 0 deletions koans-solved/asserts.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

;;; ╭╮ ╭╮ ///////
;;; ┃┃ ┃┃///////
;;; ┃┃╭┳━━┳━━╮ ┃┃╭┳━━┳━━┳━╮╭━━╮
;;; ┃┃┣┫━━┫╭╮┃ ┃╰╯┫╭╮┃╭╮┃╭╮┫━━┫
;;; ┃╰┫┣━━┃╰╯┃ ┃╭╮┫╰╯┃╭╮┃┃┃┣━━┃
;;; ╰━┻┻━━┫╭━╯/╰╯╰┻━━┻╯╰┻╯╰┻━━╯
;;; ┃┃ //////
;;; ╰╯//////

;;; Welcome to the Lisp Koans.
;;; May the code stored here influence your enlightenment as a programmer.

;;; In order to progress, fill in the blanks, denoted via ____ in source code.
;;; Sometimes, you will be asked to provide values that are equal to something.

(define-test fill-in-the-blanks
(assert-equal 2 2)
(assert-equal 3.14 3.14)
(assert-equal "Hello World" "Hello World"))

;;; Sometimes, you will be asked to say whether something is true or false,
;;; In Common Lisp, the canonical values for truth and falsehood are T and NIL.

(define-test assert-true
(assert-true t))

(define-test assert-false
(assert-false nil))

(define-test true-or-false
(true-or-false? t (= 34 34))
(true-or-false? nil (= 19 78)))

;;; Since T and NIL are symbols, you can type them in lowercase or uppercase;
;;; by default, Common Lisp will automatically upcase them upon reading.

(define-test upcase-downcase
;; Try inserting a lowercase t here.
(assert-equal t T)
;; Try inserting an uppercase NIL here.
(assert-equal NIL nil))

;;; Sometimes, you will be asked to provide a part of an expression that must be
;;; either true or false.

(define-test a-true-assertion
(assert-true (= 4 (+ 2 2))))

(define-test a-false-assertion
(assert-false (= 5 (+ 2 2))))

43 changes: 43 additions & 0 deletions koans-solved/atoms-vs-lists.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

;;; Lists in lisp are forms beginning and ending with rounded parentheses.
;;; Atoms are symbols, numbers, or other forms usually separated by whitespace
;;; or parentheses.

(define-test list-or-atom
;; The function LISTP will return true if the input is a list.
;; The function ATOM will return true if the input is an atom.
(true-or-false? t (listp '(1 2 3)))
(true-or-false? nil (atom '(1 2 3)))
(true-or-false? t (listp '("heres" "some" "strings")))
(true-or-false? nil (atom '("heres" "some" "strings")))
(true-or-false? nil (listp "a string"))
(true-or-false? t (atom "a string"))
(true-or-false? nil (listp 2))
(true-or-false? t (atom 2))
(true-or-false? t (listp '(("first" "list") ("second" "list"))))
(true-or-false? nil (atom '(("first" "list") ("second" "list")))))

(define-test the-duality-of-nil
;; The empty list, NIL, is unique in that it is both a list and an atom.
(true-or-false? t (listp nil))
(true-or-false? t (atom nil)))

(define-test keywords
;; Symbols like :HELLO or :LIKE-THIS are keywords. They are treated
;; differently in Lisp: they are constants that always evaluate to themselves.
(true-or-false? t (equal :this-is-a-keyword :this-is-a-keyword))
(true-or-false? t (equal :this-is-a-keyword ':this-is-a-keyword))
(true-or-false? nil (equal :this-is-a-keyword :this-is-also-a-keyword)))
65 changes: 65 additions & 0 deletions koans-solved/backquote.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

;;; Backquote notation is similar to quoting, except it allows for parts of the
;;; resulting expression to be "unquoted".

(define-test backquote-basics
(let ((x '(123))
(z '(7 8 9)))
;; ' quotes an expression normally.
(assert-equal ____ '(x 45 6 z))
;; ` backquotes an expression; without any unquotes, it is equivalent to
;; using the normal quote.
(assert-equal ____ `(x 45 6 z))
;; , unquotes a part of the expression.
(assert-equal ____ `(,x 45 6 z))
(assert-equal ____ `(,x 45 6 ,z))
;; ,@ splices an expression into the into the list surrounding it.
(assert-equal ____ `(,x 45 6 ,@z))
(assert-equal ____ `(,@x 45 6 ,@z))))

(define-test backquote-forms
;; Because of its properties, backquote is useful for constructing Lisp forms
;; that are macroexpansions or parts of macroexpansions.
(let ((variable 'x))
;; Fill in the blank without without using backquote/unquote notation.
(assert-equal ____
`(if (typep ,variable 'string)
(format nil "The value of ~A is ~A" ',variable ,variable)
(error 'type-error :datum ,variable
:expected-type 'string))))
(let ((error-type 'type-error)
(error-arguments '(:datum x :expected-type 'string)))
;; Fill in the blank without without using backquote/unquote notation.
(assert-equal ____
`(if (typep x 'string)
(format nil "The value of ~A is ~A" 'x x)
(error ',error-type ,@error-arguments)))))

(define-test numbers-and-words
(let ((number 5)
(word 'dolphin))
(true-or-false? ____ (equal '(1 3 5) `(1 3 5)))
(true-or-false? ____ (equal '(1 3 5) `(1 3 number)))
(assert-equal _____ `(1 3 ,number))
(assert-equal _____ `(word ,word ,word word))))

(define-test splicing
(let ((axis '(x y z)))
(assert-equal '(the axis are ____) `(the axis are ,axis))
(assert-equal '(the axis are ____) `(the axis are ,@axis)))
(let ((coordinates '((43.15 77.6) (42.36 71.06))))
(assert-equal ____ `(the coordinates are ,coordinates))
(assert-equal ____ `(the coordinates are ,@coordinates))))
112 changes: 112 additions & 0 deletions koans-solved/basic-macros.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
;;; Copyright 2013 Google Inc.
;;;
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;;
;;; http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

(define-test setf
;; SETF is a macro used to assign values to places. A place is a concept;
;; it is an abstract "somewhere" where a value is stored.
(let ((a 10)
(b (list 1 20 30 40 50))
;; We use COPY-SEQ to create a copy of a string, because using SETF to
;; modify literal data (strings, lists, etc.) is undefined behaviour.
(c (copy-seq "I am Tom.")))
;; A place may be a variable.
(setf a 1000)
(assert-equal 1000 a)
;; A place may be a part of some list.
(setf (first b) 10)
(assert-equal '(10 20 30 40 50) b)
;; A place may be a character in a string.
;; The #\x syntax denotes a single character, 'x'.
(setf (char c 5) #\B
(char c 7) #\b)
(assert-equal "I am Bob." c)
;; There are other kinds of places that we will explore in the future.
))

(define-test case
;; CASE is a simple pattern-matching macro, not unlike C's "switch".
;; It compares an input against a set of values and evaluates the code for
;; the branch where a match is found.
(let* ((a 4)
(b (case a
(3 :three)
(4 :four)
(5 :five))))
(assert-equal :four b))
;; CASE can accept a group of keys.
(let* ((c 4)
(d (case c
((0 2 4 6 8) :even-digit)
((1 3 5 7 9) :odd-digit))))
(assert-equal :even-digit d)))

(defun match-special-cases (thing)
;; T or OTHERWISE passed as the key matches any value.
;; NIL passed as the key matches no values.
;; These symbols need to passed in parentheses.
(case thing
((t) :found-a-t)
((nil) :found-a-nil)
(t :something-else)))

(define-test special-cases-of-case
;; You need to fill in the blanks in MATCH-SPECIAL-CASES.
(assert-equal :found-a-t (match-special-cases t))
(assert-equal :found-a-nil (match-special-cases nil))
(assert-equal :something-else (match-special-cases 42)))

(define-test your-own-case-statement
;; We use FLET to define a local function.
(flet ((cartoon-dads (input)
(case input
;; Fill in the blanks with proper cases.
(:bart :homer)
(:stewie :peter)
(:stan :randy)
(:this-one-doesnt-happen :fancy-cat)
(t :unknown))))
(assert-equal (cartoon-dads :bart) :homer)
(assert-equal (cartoon-dads :stewie) :peter)
(assert-equal (cartoon-dads :stan) :randy)
(assert-equal (cartoon-dads :space-ghost) :unknown)))

(define-test limits-of-case
;; So far, we have been comparing objects using EQUAL, one of the Lisp
;; comparison functions. CASE compares the keys using EQL, which is distinct
;; from EQUAL.
;; EQL is suitable for comparing numbers, characters, and objects for whom we
;; want to check verify they are the same object.
(let* ((string "A string")
(string-copy (copy-seq string)))
;; The above means that two distinct strings will not be the same under EQL,
;; even if they have the same contents.
(true-or-false? nil (eql string string-copy))
(true-or-false? t (equal string string-copy))
;; The above also means that CASE might give surprising results when used on
;; strings.
(let ((match (case string
("A string" :matched)
(t :not-matched))))
(assert-equal :not-matched match))
;; We will explore this topic further in the EQUALITY-DISTINCTIONS lesson.
))

(define-test cond
;; COND is similar to CASE, except it is more general. It accepts arbitrary
;; conditions and checks them in order until one of them is met.
(let* ((number 4)
(result (cond ((> number 0) :positive)
((< number 0) :negative)
(t :zero))))
(assert-equal :positive result)))
Loading

0 comments on commit eab7b89

Please sign in to comment.