diff --git a/.koans b/.koans index da971924..bf11e5fb 100644 --- a/.koans +++ b/.koans @@ -1,31 +1,32 @@ ( - :asserts - :nil-false-empty - :evaluation - :atoms-vs-lists - :special-forms - :lists - :arrays - :vectors - :multiple-values - :equality-distinctions - :hash-tables - :functions - :strings - :structures - :iteration - :mapcar-and-reduce - :control-statements - :condition-handlers - :loops - :triangle-project - :scoring-project - :format - :type-checking - :clos - :std-method-comb - :dice-project - :macros - :scope-and-extent - #+quicklisp :threads + #:asserts + #:nil-false-empty + #:evaluation + #:atoms-vs-lists + #:let + #:basic-macros + #:lists + #:arrays + #:vectors + #:multiple-values + #:equality-distinctions + #:hash-tables + #:functions + #:strings + #:structures + #:iteration + #:mapcar-and-reduce + #:control-statements + #:condition-handlers + #:loops + #:triangle-project + #:scoring-project + #:format + #:type-checking + #:clos + #:std-method-comb + #:dice-project + #:macros + #:scope-and-extent + #+quicklisp #:threads ) diff --git a/koans/arrays.lisp b/koans/arrays.lisp index 873a1579..c00c17fa 100644 --- a/koans/arrays.lisp +++ b/koans/arrays.lisp @@ -1,46 +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. - - -;; see http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node157.html +;;; 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. +;;; See http://www.gigamonkeys.com/book/collections.html (define-test test-basic-array-stuff - " the first block of code defines an 8x8 array, then fills - the elements with a checkerboard pattern" - (let ((chess-board)) - (setf chess-board (make-array '(8 8))) - "this dotimes is an iterator which loops x over integers 0 to 7" + "We define 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) - (if (evenp (+ x y)) - (setf (aref chess-board x y) :black) - (setf (aref chess-board x y) :white) - ))) + "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 (aref chess-board 0 0) ___) - (assert-equal (aref chess-board 2 3) ___) - "array-rank returns the number of dimensions of the array" - (assert-equal ___ (array-rank chess-board)) - "array-dimensions returns a list of the cardinality of the array dims" - (assert-equal ___ (array-dimensions chess-board)) - (assert-equal ___ (array-total-size chess-board)))) + (assert-equal (aref chess-board 0 0) ____) + (assert-equal (aref chess-board 2 3) ____) + "ARRAY-RANK returns the number of dimensions of the array." + (assert-equal ____ (array-rank chess-board)) + "ARRAY-DIMENSIONS returns a list of the cardinality of the array dims" + (assert-equal ____ (array-dimensions chess-board)) + "ARRAY-TOTAL-SIZE returns the total number of elements in the array." + (assert-equal ____ (array-total-size chess-board)))) (define-test test-make-your-own-array - "make your own array that meets the specifications below." - (let ((color-cube nil)) - "you may need to modify your array after you make it" + "Make your own array that meets the specifications below." + (let ((color-cube ____)) + "You may need to modify your array after you create it." + (setf (____ color-cube ____ ____ ____) ____ + (____ color-cube ____ ____ ____) ____) (if (typep color-cube '(simple-array T (3 3 3))) (progn (assert-equal 3 (array-rank color-cube)) @@ -50,28 +47,24 @@ (assert-equal (aref color-cube 2 1 0) :white)) (assert-true nil)))) - (define-test test-adjustable-array - "one may build arrays that can change size" + "The size of an array does not need to be constant." (let ((x (make-array '(2 2) :initial-element 5 :adjustable t))) (assert-equal (aref x 1 0) ____) (assert-equal (array-dimensions x) ____) (adjust-array x '(3 4)) (assert-equal (array-dimensions x) ____))) - (define-test test-make-array-from-list - (let ((x)) - (setf x (make-array '(4) :initial-contents '(:one :two :three :four))) + "One can create arrays from list structure." + (let ((x (make-array '(4) :initial-contents '(:one :two :three :four)))) (assert-equal (array-dimensions x) ____) (assert-equal ____ (aref x 0)))) - (define-test 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 nil)) - (setf my-array (make-array '(2 2 2 2))) + "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 (aref my-array 0 0 0 0) ____) diff --git a/koans/asserts.lisp b/koans/asserts.lisp index 806ac3a6..8093e150 100644 --- a/koans/asserts.lisp +++ b/koans/asserts.lisp @@ -1,47 +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. +;;; 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. +;;; ╭╮ ╭╮ /////// +;;; ┃┃ ┃┃/////// +;;; ┃┃╭┳━━┳━━╮ ┃┃╭┳━━┳━━┳━╮╭━━╮ +;;; ┃┃┣┫━━┫╭╮┃ ┃╰╯┫╭╮┃╭╮┃╭╮┫━━┫ +;;; ┃╰┫┣━━┃╰╯┃ ┃╭╮┫╰╯┃╭╮┃┃┃┣━━┃ +;;; ╰━┻┻━━┫╭━╯/╰╯╰┻━━┻╯╰┻╯╰┻━━╯ +;;; ┃┃ ////// +;;; ╰╯////// -; Concept: What do you do to go through the lisp koans? You fill in -; the blanks, or otherwise fix the lisp code so that the -; code within the 'define-test' blocks passes. +;;; 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. -; In common lisp, "True" and "False" are represented by "t" and "nil". -; More in a future lesson, but for now, consider t to be true, -; and nil to be false. +(define-test fill-in-the-blanks + (assert-equal ____ 2) + (assert-equal ____ 3.14) + (assert-equal ____ "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 - "t is true. Replace the blank with a t" - (assert-true ___)) + (assert-true ____)) (define-test assert-false - "nil is false" - (assert-false ___)) + (assert-false ____)) -(define-test fill-in-the-blank - "sometimes you will need to fill the blank to complete" - (assert-equal 2 ___)) +(define-test true-or-false + (true-or-false? ____ (= 34 34)) + (true-or-false? ____ (= 19 78))) -(define-test fill-in-the-blank-string - (assert-equal ___ "hello world")) +;;; 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 test-true-or-false - "sometimes you will be asked to evaluate whether statements - are true (t) or false (nil)" - (true-or-false? ___ (equal 34 34)) - (true-or-false? ___ (equal 19 78))) +(define-test upcase-downcase + ;; Try inserting a lowercase t here. + (assert-equal ____ T) + ;; Try inserting an uppercase NIL here. + (assert-equal ____ 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 (= ____ (+ 2 2)))) + +(define-test a-false-assertion + (assert-false (= ____ (+ 2 2)))) diff --git a/koans/atoms-vs-lists.lisp b/koans/atoms-vs-lists.lisp index ce49da87..62de29f1 100644 --- a/koans/atoms-vs-lists.lisp +++ b/koans/atoms-vs-lists.lisp @@ -1,48 +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. - - -(define-test test-list-or-atom - "Lists in lisp are forms beginning and ending with rounded parentheses. - Atoms are symbols, numbers, or other forms usually separated by - white-space or parentheses. 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? ___ (listp '(1 2 3))) - (true-or-false? ___ (atom '(1 2 3))) - - (true-or-false? ___ (listp '("heres" "some" "strings"))) - (true-or-false? ___ (atom '("heres" "some" "strings"))) - - (true-or-false? ___ (listp "a string")) - (true-or-false? ___ (atom "a string")) - - (true-or-false? ___ (listp 2)) - (true-or-false? ___ (atom 2)) - - (true-or-false? ___ (listp '(("first" "list") ("second" "list")))) - (true-or-false? ___ (atom '(("first" "list") ("second" "list"))))) - - -(define-test test-empty-list-is-both-list-and-atom - "the empty list, nil, is unique in that it is both a list and an atom" - (true-or-false? ___ (listp nil)) - (true-or-false? ___ (atom nil))) - - -(define-test test-keywords - "symbols like :hello or :like-this are treated differently in lisp. - Called keywords, they are symbols that evaluate to themselves." - (true-or-false? ___ (equal :this-is-a-keyword :this-is-a-keyword)) - (true-or-false? ___ (equal :this-is-a-keyword ':this-is-a-keyword))) +;;; 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? ____ (listp '(1 2 3))) + (true-or-false? ____ (atom '(1 2 3))) + (true-or-false? ____ (listp '("heres" "some" "strings"))) + (true-or-false? ____ (atom '("heres" "some" "strings"))) + (true-or-false? ____ (listp "a string")) + (true-or-false? ____ (atom "a string")) + (true-or-false? ____ (listp 2)) + (true-or-false? ____ (atom 2)) + (true-or-false? ____ (listp '(("first" "list") ("second" "list")))) + (true-or-false? ____ (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? ____ (listp nil)) + (true-or-false? ____ (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? ____ (equal :this-is-a-keyword :this-is-a-keyword)) + (true-or-false? ____ (equal :this-is-a-keyword ':this-is-a-keyword)) + (true-or-false? ____ (equal :this-is-a-keyword :this-is-also-a-keyword))) diff --git a/koans/basic-macros.lisp b/koans/basic-macros.lisp new file mode 100644 index 00000000..dc1d8dd0 --- /dev/null +++ b/koans/basic-macros.lisp @@ -0,0 +1,117 @@ +;;; 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 ____ a) + ;; A place may be a part of some list. + (setf (first b) 10) + (assert-equal ____ 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 ____ 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 ____ 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 ____ 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 + (____ :found-a-t) + (____ :found-a-nil) + (____ :something-else))) + +(define-test special-cases-of-case + ;; You need to fill in the blanks in MATCH-SPECIAL-CASES. + (assert-equal :found-a-t (case-special-symbols-match t)) + (assert-equal :found-a-nil (case-special-symbols-match nil)) + (assert-equal :something-else (case-special-symbols-match 42))) + +(defun cartoon-dads (input) + (case input + ;; Fill in the blanks with proper cases. + ____ + ____ + ____ + (:this-one-doesnt-happen :fancy-cat) + (t :unknown))) + +(define-test your-own-case-statement + ;; You need to fill in the blanks in CARTOON-DADS. + (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? ____ (eql string string-copy)) + (true-or-false? ____ (equal string string-copy)) + ;; The above also means that CASE might give surprising results when used on + ;; strings. + (let ((match-1 (case string + (string-copy :matched) + (t :not-matched))) + (match-2 (case string + (string :matched) + (t :not-matched)))) + (assert-equal ____ match-1) + (assert-equal ____ match-2)) + ;; 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 ____ result))) diff --git a/koans/evaluation.lisp b/koans/evaluation.lisp index 47e56c7e..176b3187 100644 --- a/koans/evaluation.lisp +++ b/koans/evaluation.lisp @@ -1,64 +1,66 @@ -;; 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. +;;; 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. +;;; In most imperative languages, the syntax of a function call has the function +;;; name succeeded by a list of arguments. In Lisp, the function name and +;;; arguments are all part of the same list, with the function name the first +;;; element of that list. -;; Based on "Successful Lisp" by David B. Lamkins -;; Download link to archive https://successful-lisp.blogspot.com/p/httpsdrive.html -;; Or buy the Book via : https://successful-lisp.blogspot.com/ +(define-test function-names + ;; In these examples, +, -, *, and / are function names. + (assert-equal ____ (+ 2 3)) + (assert-equal ____ (- 1 3)) + (assert-equal ____ (* 7 4)) + (assert-equal ____ (/ 100 4))) +(define-test numberp + ;; NUMBERP is a predicate which returns true if its argument is a number. + (assert-equal ____ (numberp 5)) + (assert-equal ____ (numberp 2.0)) + (assert-equal ____ (numberp "five"))) -(define-test test-function-name-is-first-argument - "In most imperative languages, the syntax of a function call has - the function name succeeded by a list of arguments. In lisp, - the function name and arguments are all part of the same list, - with the function name the first element of that list." +(define-test evaluation-order + ;; Arguments to a function are evaluated before the function is called. + (assert-equal ____ (* (+ 1 2) (- 13 10)))) - "in these examples, the function names are +, -, and *" - (assert-equal ___ (+ 2 3)) - (assert-equal ___ (- 1 3)) - (assert-equal ___ (* 7 4)) - "'>' and '=' are the boolean functions (predicates) 'greater-than' and - 'equal to'" - (assert-equal ___ (> 100 4)) - (assert-equal ___ (= 3 3)) - "'NUMBERP' is a predicate which returns true if the argument is a number" - (assert-equal ___ (numberp 5)) - (assert-equal ___ (numberp "five"))) +(define-test basic-arithmetic + ;; The below functions are boolean functions (predicates) that operate on + ;; numbers. + (assert-equal ____ (> 25 4)) + (assert-equal ____ (< 8 2)) + (assert-equal ____ (= 3 3)) + (assert-equal ____ (<= 6 (/ 12 2))) + (assert-equal ____ (>= 20 (+ 1 2 3 4 5))) + (assert-equal ____ (/= 15 (+ 4 10)))) - -(define-test test-evaluation-order - "Arguments to functions are evaluated before the function" - (assert-equal ___ (* (+ 1 2) (- 13 10)))) - - -(define-test test-quoting-behavior - "Preceding a list with a quote (') will tell lisp not to evaluate a list. - The quote special form suppresses normal evaluation, and instead returns - the literal list. - Evaluating the form (+ 1 2) returns the number 3, - but evaluating the form '(+ 1 2) returns the list (+ 1 2)" +(define-test quote + ;; Preceding a list with a quote (') will tell Lisp not to evaluate a list. + ;; The quote special form suppresses normal evaluation, and instead returns + ;; the literal list. + ;; Evaluating the form (+ 1 2) returns the number 3, but evaluating the form + ;; '(+ 1 2) returns the list (+ 1 2). (assert-equal ____ (+ 1 2)) (assert-equal ____ '(+ 1 2)) - "'LISTP' is a predicate which returns true if the argument is a list - the '(CONTENTS) form defines a list literal containing CONTENTS" - (assert-equal ___ (listp '(1 2 3))) - (assert-equal ___ (listp 100)) - (assert-equal ___ (listp "Word to your moms I came to drop bombs")) - (assert-equal ___ (listp nil)) - (assert-equal ___ (listp (+ 1 2))) - (assert-equal ___ (listp '(+ 1 2))) - "equalp is an equality predicate" - (assert-equal ___ (equalp 3 (+ 1 2))) - "the '(xyz ghi) syntax is syntactic sugar for the (QUOTE (xyz ghi)) function." - (true-or-false? ___ (equalp '(/ 4 0) (quote (/ 4 0))))) + (assert-equal ____ (list '+ 1 2)) + ;; The 'X syntax is syntactic sugar for (QUOTE X). + (true-or-false? ____ (equal '(/ 4 0) (quote (/ 4 0))))) + +(define-test listp + ;; LISTP is a predicate which returns true if the argument is a list. + (assert-equal ____ (listp '(1 2 3))) + (assert-equal ____ (listp 100)) + (assert-equal ____ (listp "Hello world")) + (assert-equal ____ (listp nil)) + (assert-equal ____ (listp (+ 1 2))) + (assert-equal ____ (listp '(+ 1 2)))) diff --git a/koans/let.lisp b/koans/let.lisp new file mode 100644 index 00000000..fef4a9a2 --- /dev/null +++ b/koans/let.lisp @@ -0,0 +1,62 @@ +;;; 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 let + ;; The LET form establishes a lexical extent within which new variables are + ;; created: a symbol that names a variable becomes bound to a value. + (let ((x 10) + (y 20)) + (assert-equal (+ x y) ____) + ;; It is possible to shadow previously visible bindings. + (let ((y 30)) + (assert-equal (+ x y) ____)) + (assert-equal (+ x y) ____)) + ;; Variables bound by LET have a default value of NIL. + (let (x) + (assert-equal x ____))) + +(define-test let-versus-let* + ;; LET* is similar to LET, except the bindings are established sequentially, + ;; and a binding may use bindings that were established before it. + (let ((x 10) + (y 20)) + (let ((x (+ y 100)) + (y (+ x 100))) + (assert-equal ____ x) + (assert-equal ____ y)) + (let* ((x (+ y 100)) + (y (+ x 100))) + ;; Which X is used to compute the value of Y? + (assert-equal ____ x) + (assert-equal ____ y)))) + +(define-test let-it-be-equal + ;; Fill in the LET and LET* to get the tests to pass. + (let ((a 1) + (b :two) + (c "Three")) + (let ((____ ____) + (____ ____) + (____ ____)) + (assert-equal a 100) + (assert-equal b 200) + (assert-equal c "Jellyfish")) + (let* ((____ ____) + (____ ____) + ;; In this third binding, you are allowed to use the variables bound + ;; by the previous two LET* bindings. + (____ ____)) + (assert-equal a 121) + (assert-equal b 200) + (assert-equal c (+ a (/ b a)))))) diff --git a/koans/lists.lisp b/koans/lists.lisp index e64f88fa..5cdc4e69 100644 --- a/koans/lists.lisp +++ b/koans/lists.lisp @@ -1,109 +1,156 @@ -;; 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. - - -;; based on python koans 'about_lists.py' -;; based also on "Lisp 3rd Edition" ch. 17. "List storage, surgery and reclamation" - - -(define-test test-creating-lists - "lists can be created using the quote form, or the 'list' function" - (let ((fruits nil) - (some-evens nil)) - (setf fruits '(orange pomello clementine)) - (setf some-evens (list (* 2 1) (* 2 2) (* 2 3))) - (assert-equal fruits ___) - (assert-equal ___ (length some-evens)))) - - -(define-test test-list-cons - "cons CONStructs new lists, by prefixing some list with - a new element like (cons new-element some-list)" - (let ((nums nil)) - (setf nums (cons :one nums)) - (assert-equal '(:one) nums) - - (setf nums (cons :two nums)) - (assert-equal ___ nums) - - "lists can contain anything, even mixtures of different things" - (setf nums (cons 333 nums)) - (assert-equal ___ nums) - - "lists can of course contain lists" - (setf nums (cons '("the" "rest") nums)) - (assert-equal ___ nums))) - - -(define-test test-push-pop - (let ((stack '(10 20 30 40)) - (firstval nil)) - "push adds an element to the beginning of a list referred to by some symbol" - (push "last" stack) - (assert-equal '("last" 10 20 30 40) stack) - - "pop is the opposite of push. - It removes and returns the first element of a list" - (setf firstval (pop stack)) - (assert-equal "last" firstval) - (assert-equal '(10 20 30 40) stack) - - (setf firstval (pop stack)) - (assert-equal ___ firstval) - (assert-equal ___ stack))) - - -(define-test test-append - "append attaches one list to the end of another." - (assert-equal '(:a :b :c) (append '(:a :b) '(:c))) - - (let ((abc '(:a :b :c)) - (xyz '(:x :y :z)) - (abcxyz nil)) - (setf abcxyz (append abc xyz)) - (assert-equal ___ abc) - (assert-equal ___ xyz) - (assert-equal ___ abcxyz))) - +;;; 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. + +;;; A singly linked list is the basic build block of Lisp. Each node of such a +;;; list is called a "cons cell" in Lisp. Each cons cell has two slots: a CAR, +;;; often used to hold an element of a list, and a CDR, often used to reference +;;; the next cons cell. + +(define-test how-to-make-lists + (let (;; Literal lists can be passed by quoting them. + (fruits '(orange pomello clementine)) + ;; Freshly constructed lists can be passed using the LIST function. + (some-evens (list (* 2 1) (* 2 2) (* 2 3))) + ;; Lists can also be passed using quotes and dot notation... + (long-numbers '(16487302 . (3826700034 . (10000000 . '())))) + ;; ...or by using the function CONS. + (names (cons "Matthew" (cons "Mark" (cons "Margaret" '()))))) + ;; Try filling in the below blanks in different ways. + (assert-equal ____ fruits) + (assert-equal ____ some-evens) + (assert-equal ____ long-numbers) + (assert-equal ____ names))) + +(define-test cons-tructing-lists + ;; The function CONS can be used to add new elements at the beginning of + ;; an existing list. + (let ((nums '())) + (setf nums (cons :one nums)) + (assert-equal ____ nums) + (setf nums (cons :two nums)) + (assert-equal ____ nums) + ;; Lists can contain anything, even objects of different types. + (setf nums (cons 333 nums)) + (assert-equal ____ nums) + ;; Lists can contain other lists, too. + (setf nums (cons (list "some" "strings") nums)) + (assert-equal ____ nums))) + +(define-test car-and-cdr + ;; We may use functions CAR and CDR (or, alternatively, FIRST and REST) to + ;; access the two slots of a cons cell. + (let ((x (cons 1 2))) + (assert-equal ____ (car x)) + (assert-equal ____ (cdr x))) + ;; Calls to CAR and CDR are often intertwined to extract data from a nested + ;; cons structure. + (let ((structure '((1 2) (("foo" . "bar"))))) + (assert-equal ____ (car x)) + (assert-equal ____ (car (cdr x))) + (assert-equal ____ (cdr (car (car (cdr x))))) + ;; Lisp defines shorthand functions for up to four such nested calls. + (assert-equal ____ (car x)) + (assert-equal ____ (cadr x)) + (assert-equal ____ (cdaadr x)))) + +(define-test cons-tructing-improper-lists + ;; A proper list is a list whose final CDR ends with NIL. + ;; An improper list either has a non-NIL value in its final CDR or does not + ;; have a final CDR due to a cycle in its structure. + (let (;; We can construct non-cyclic improper lists using LIST*... + (x (list* 1 2 3 4 5)) + ;; ...or pass them as literals via dot notation. + (y '(6 7 8 9 . 0))) + ;; The function LAST returns the last cons cell of a list. + (assert-equal ____ (last x)) + (assert-equal ____ (list y))) + ;; We can create a cyclic list by changing the last CDR of a list to refer to + ;; another cons cell + (let ((list (list 1 2 3 4 5)) + (cyclic-list (list 1 2 3 4 5))) + (setf (cdr (last cyclic-list)) cyclic-list) + ;; Function LIST-LENGTH returns NIL if a list is cyclic. + (assert-equal ____ (list-length list)) + (assert-equal ____ (list-length cyclic-list)) + ;; Many Lisp functions operate only on proper lists. + ;; The function NTH is not one of them; it can be used to retrieve elements + ;; of cyclic lists. + (assert-equal ____ (nth 101 cyclic-list)))) + +(define-test push-pop + ;; PUSH and POP are macros similar to SETF, as both of them operate on places. + (let ((place '(10 20 30 40))) + ;; PUSH sets the value of the place to a new cons cell containing some value + ;; in its CAR. + (push 0 place) + (assert-equal ____ place) + ;; POP removes a single cons cell from a place, sets the place to its CDR, + ;; and returns the value from its CAR. + (let ((value (pop place))) + (assert-equal ____ value) + (assert-equal ____ place)) + ;; The return value of POP can be discarded to simply "remove" a single cons + ;; cell from a place. + (pop place) + (let ((value (pop place))) + (assert-equal ____ value) + (assert-equal ____ place)))) + +(define-test append-nconc + ;; The functions APPEND and NCONC appends one list to the end of another. + ;; While APPEND creates new lists, NCONC modifies existing ones; therefore + ;; APPEND can be used on literals, but NCONC needs fresh lists. + (assert-equal ____ (append '(:a :b) '(:c))) + (assert-equal ____ (nconc (list :a :b) (list :c))) + (let ((list-1 (list 1 2 3)) + (list-2 (list 4 5 6))) + ;; Both APPEND and NCONC return the appended list, but the interesting part + ;; is what happens when we try to use the original variables passed to them. + (assert-equal ____ (append list-1 list-2)) + (assert-equal ____ list-1) + (assert-equal ____ list-2) + (assert-equal ____ (nconc list-1 list-2)) + (assert-equal ____ list-1) + (assert-equal ____ list-2))) (define-test test-accessing-list-elements - (let ((noms '("peanut" "butter" "and" "jelly"))) - (assert-equal "peanut" (first noms)) - (assert-equal ___ (second noms)) - (assert-equal ___ (fourth noms)) - "last returns a singleton list of the final element" - (assert-equal ___ (last noms)) - (assert-equal "butter" (nth 1 noms)) ; k 1 - (assert-equal ___ (nth 0 noms)) - (assert-equal ___ (nth 2 noms)) - "'elt' is similar to 'nth', with the arguments reversed" - (assert-equal ___ (elt noms 2)))) + (let ((noms '("peanut" "butter" "and" "jelly"))) + (assert-equal "peanut" (first noms)) + (assert-equal ___ (second noms)) + (assert-equal ___ (fourth noms)) + "last returns a singleton list of the final element" + (assert-equal ___ (last noms)) + (assert-equal "butter" (nth 1 noms)) ; k 1 + (assert-equal ___ (nth 0 noms)) + (assert-equal ___ (nth 2 noms)) + "'elt' is similar to 'nth', with the arguments reversed" + (assert-equal ___ (elt noms 2)))) (define-test test-slicing-lists - (let ((noms '("peanut" "butter" "and" "jelly"))) - (assert-equal ___ (subseq noms 0 1)) - (assert-equal ___ (subseq noms 0 2)) - (assert-equal ___ (subseq noms 2 2)) - (assert-equal ___ (subseq noms 2)))) + (let ((noms '("peanut" "butter" "and" "jelly"))) + (assert-equal ___ (subseq noms 0 1)) + (assert-equal ___ (subseq noms 0 2)) + (assert-equal ___ (subseq noms 2 2)) + (assert-equal ___ (subseq noms 2)))) (define-test test-list-breakdown - "car (aka. 'first') returns the first value in a list" + "car (aka. 'first') returns the first value in a list" (assert-equal ___ (car '(1 2 3))) (assert-equal ___ (car nil)) - "cdr (aka. 'rest') refers to the remainder of the list, + "cdr (aka. 'rest') refers to the remainder of the list, after the first element" (assert-equal ___ (cdr '(1 2 3))) (assert-equal ___ (cdr nil))) diff --git a/koans/nil-false-empty.lisp b/koans/nil-false-empty.lisp index ca06edf3..6d4dd412 100644 --- a/koans/nil-false-empty.lisp +++ b/koans/nil-false-empty.lisp @@ -1,55 +1,52 @@ -;; 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 test-t-and-nil-are-opposites - "not is a function which returns the boolean opposite of its argument" - (true-or-false? ___ (not nil)) - (true-or-false? ___ (not t))) - - -(define-test test-nil-and-empty-list-are-the-same-thing - (true-or-false? ___ ()) - (true-or-false? ___ (not ()))) - - -(define-test test-lots-of-things-are-true - " every value, other than nil, is boolean true" - (true-or-false? ___ 5) - (true-or-false? ___ (not 5)) - (true-or-false? ___ "A String") - "only nil is nil. Everything else is effectively true." - "the empty string" - (true-or-false? ___ "") - "a list containing a nil" - (true-or-false? ___ '(nil)) - "an array with no elements" - (true-or-false? ___ (make-array '(0))) - "the number zero" - (true-or-false? ___ 0)) - - -(define-test test-and - "and can take multiple arguments" - (true-or-false? ___ (and t t t t t)) - (true-or-false? ___ (and t t nil t t)) - "if no nils, and returns the last value" - (assert-equal ___ (and t t t t t 5))) - - -(define-test test-or - "or can also take multiple arguments" - (true-or-false? ____ (or nil nil nil t nil)) - "or returns the first non nil value, or nil if there are none." - (assert-equal ____ (or nil nil nil)) - (assert-equal ____ (or 1 2 3 4 5))) \ No newline at end of file +;;; 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 t-and-nil-are-opposites + ;; NOT is a function which returns the boolean opposite of its argument. + (true-or-false? ____ (not nil)) + (true-or-false? ____ (not t))) + +(define-test nil-and-empty-list-are-the-same-thing + ;; In Common Lisp, NIL is also the empty list. + (true-or-false? ____ '()) + (true-or-false? ____ (not '()))) + +(define-test in-lisp-many-things-are-true + ;; In Common Lisp, the canonical values for truth is T. + ;; However, everything that is non-NIL is true, too. + (true-or-false? ____ 5) + (true-or-false? ____ (not 5)) + (true-or-false? ____ "a string") + ;; Even an empty string... + (true-or-false? ____ "") + ;; ...or a list containing a NIL... + (true-or-false? ____ (list nil)) + ;; ...or an array with no elements... + (true-or-false? ____ (make-array 0)) + ;; ...or the number zero. + (true-or-false? ____ 0)) + +(define-test and + ;; The logical operator AND can take multiple arguments. + (true-or-false? ____ (and t t t t t)) + (true-or-false? ____ (and t t nil t t)) + ;; If all values passed to AND are true, it returns the last value. + (assert-equal ____ (and t t t t t 5))) + +(define-test or + ;; The logical operator OR can also take multiple arguments. + (true-or-false? ____ (or nil nil nil t nil)) + ;; OR returns the first non-NIL value it encounters, or NIL if there are none. + (assert-equal ____ (or nil nil nil)) + (assert-equal ____ (or 1 2 3 4 5))) diff --git a/koans/special-forms.lisp b/koans/special-forms.lisp deleted file mode 100644 index e9959fed..00000000 --- a/koans/special-forms.lisp +++ /dev/null @@ -1,147 +0,0 @@ -;; 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. - - -; Special forms are evaluatable lisp forms (lists) which are -; neither functions nor macros. Here is an introduction to a -; few of them. - -; based on http://psg.com/~dlamkins/sl/chapter03-03.html - -(defvar my-name) -(defvar my-clones-name) -(defvar a) -(defvar b) -(defvar c 0) - -(define-test test-setf - "setf is used to assign values to symbols. These symbols may refer to - variables with lexical or dynamic scope." - (setf my-name "David") - (assert-equal my-name ____) - " In SBCL, if the symbol isn't defined as a variable, via a top-level defvar - or let statement, the setf call may result in a warning." - (setf my-clones-name my-name) - (assert-equal "David" ____) - (setf a 5) - (setf b 10) - (setf c ___) - (assert-equal 50 c)) - - -(define-test test-let - "The let form establishes a lexical extent, within which explicit symbols - may be bound to values. The binding only extends over the extent of the - lexical form. After which, the previous value, if it exists, is visible again." - (setf a 10) - (setf b 20) - (assert-equal a ___) - (assert-equal b ___) - (let ((a 1111) - (b 2222)) - (assert-equal a ___) - (assert-equal b ___)) - (assert-equal a ___) - (assert-equal b ___)) - - -(define-test test-let-default-value - "let vars have a default value" - (let ((x)) - (assert-equal ___ x))) - -(define-test test-let-bindings-are-parallel - "When defining the bindings in the let form, later bindings may not depend - on earlier ones" - (setf a 100) - (let ((a 5) - (b (* 10 a))) - (assert-equal b ___))) - -(define-test test-let*-bindings-are-series - "let* is like let, but successive bindings may use values of previous ones" - (setf a 100) - (let* ((a 5) - (b (* 10 a))) - (assert-equal b ___)) - (assert-equal a ___)) - - -(define-test write-your-own-let-statement - "fix the let statement to get the tests to pass" - (setf a 100) - (setf b 23) - (setf c 456) - (let ((a __) - (b __) - (c __)) - (assert-equal a 100) - (assert-equal b 200) - (assert-equal c "Jellyfish")) - (let* ((a __) - ;; add more here - ) - (assert-equal a 121) - (assert-equal b 200) - (assert-equal c (+ a (/ b a))))) - -(define-test test-case - "the case form is like the C switch statement: it - compares an input with a set of values and evaluates an - expression once a match is found" - (setf a 4) - (setf b - (case a (4 :four) - (5 :five) - ;; t specifies default behavior - (t :unknown))) - (assert-equal ____ b) - "case can also check if a list of values contains - the input" - (setf c - (case a (5 :five) - ((3 4) :three-or-four))) - (assert-equal ____ c)) - -(defun cartoon-dads (input) - "you should be able to complete this case statement" - (case input (:this-one-doesnt-happen :fancy-cat) - (t :unknown))) - -(define-test test-your-own-case-statement - "fix this by completing the 'cartoon-dads' function above" - (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 test-limits-of-case - "case is not suitable for all kinds of values, because - it uses the function eql for comparisons. We will explore - the implications of this in the equality-distinctions lesson" - (let* ((name "John") - (lastname (case name ("John" "Doe") - ("Max" "Mustermann") - (t "Anonymous")))) - (assert-equal ____ lastname))) - -(define-test test-cond - "cond is the general purpose form for checking multiple - conditions, until a condition is met" - (setf a 4) - (setf c - (cond ((> a 0) :positive) - ((< a 0) :negative) - (t :zero))) - (assert-equal ____ c)) diff --git a/test-framework.lisp b/test-framework.lisp index c9d30900..20137153 100644 --- a/test-framework.lisp +++ b/test-framework.lisp @@ -122,31 +122,31 @@ ;;; Assert macros -(defmacro assert-eq (expected form) +(defmacro assert-eq (form expected) "Assert whether expected and form are EQ." `(expand-assert :equal ,form ,form ,expected :test #'eq)) -(defmacro assert-eql (expected form) +(defmacro assert-eql (form expected) "Assert whether expected and form are EQL." `(expand-assert :equal ,form ,form ,expected :test #'eql)) -(defmacro assert-equal (expected form) +(defmacro assert-equal (form expected) "Assert whether expected and form are EQUAL." `(expand-assert :equal ,form ,form ,expected :test #'equal)) -(defmacro assert-equalp (expected form) +(defmacro assert-equalp (form expected) "Assert whether expected and form are EQUALP." `(expand-assert :equal ,form ,form ,expected :test #'equalp)) -(defmacro true-or-false? (expected form) +(defmacro true-or-false? (form expected) "Assert whether expected and form are logically equivalent." - `(expand-assert :equal ,form (not (not ,form)) ,expected :test #'equal)) + `(expand-assert :equal ,form (notnot ,form) ,(notnot expected) :test #'eql)) -(defmacro assert-error (condition form) +(defmacro assert-error (form condition) "Assert whether form signals condition." `(expand-assert :error ,form (handler-case ,form (error (e) e)) ,condition)) -(defmacro assert-expands (expansion form) +(defmacro assert-expands (form expansion) "Assert whether form expands to expansion." `(expand-assert :macro ,form (macroexpand-1 ',form) ,expansion)) @@ -156,7 +156,7 @@ (defmacro assert-true (form) "Assert whether the form is true." - `(expand-assert :result ,form ,form t :test #'notnot)) + `(expand-assert :result ,form ,(notnot form) t)) ;;; Run the tests