From 54bc26b8821b32e4a1c2c6a94756a11732f5bcf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=22phoe=22=20Herda?= Date: Wed, 6 May 2020 19:59:26 +0200 Subject: [PATCH] Even more koans fixed --- koans/control-statements.lisp | 22 ++--- koans/functions.lisp | 159 ++++++++++++++-------------------- koans/multiple-values.lisp | 2 +- koans/strings.lisp | 123 +++++++++++++------------- 4 files changed, 137 insertions(+), 169 deletions(-) diff --git a/koans/control-statements.lisp b/koans/control-statements.lisp index 3e071dcc..8db29d6f 100644 --- a/koans/control-statements.lisp +++ b/koans/control-statements.lisp @@ -12,16 +12,18 @@ ;; See the License for the specific language governing permissions and ;; limitations under the License. +;; TODO return-from + (define-test test-if-then-else - (let ((result)) - (if t - (setf result "true value") - (setf result "false value")) - (assert-equal result ____) - (if nil - (setf result "true value") - (setf result "false value")) - (assert-equal result ____))) + (let ((result)) + (if t + (setf result "true value") + (setf result "false value")) + (assert-equal result ____) + (if nil + (setf result "true value") + (setf result "false value")) + (assert-equal result ____))) (define-test test-when-and-unless @@ -65,4 +67,4 @@ (setf x (+ 1 x)) nil (setf x (+ 1 x))) - x))) \ No newline at end of file + x))) diff --git a/koans/functions.lisp b/koans/functions.lisp index 0dda2fb0..2b14d116 100644 --- a/koans/functions.lisp +++ b/koans/functions.lisp @@ -98,112 +98,83 @@ ;; A &rest parameter must come before &key parameters. (list a b c c-provided-p x)) -(define-test test-many-kinds-params +(define-test funky-parameters (assert-equal (func-with-funky-parameters 1) ___) (assert-equal (func-with-funky-parameters 1 :b 2) ___) (assert-equal (func-with-funky-parameters 1 :b 2 :c 3) ___) (assert-equal (func-with-funky-parameters 1 :c 3 :b 2) ___)) -(define-test test-lambdas-are-nameless-functions - "A lambda form defines a function, but with no name. It is possible - to execute that function immediately, or put it somewhere for later use." - (assert-equal 19 ((lambda (a b) (+ a b)) 10 9)) - (let ((my-function)) - (setf my-function (lambda (a b) (* a b))) - (assert-equal ___ (funcall my-function 11 9))) - (let ((list-of-functions nil)) - (push (lambda (a b) (+ a b)) list-of-functions) - (push (lambda (a b) (* a b)) list-of-functions) - (push (lambda (a b) (- a b)) list-of-functions) - (assert-equal ___ (funcall (second list-of-functions) 2 33)))) - -(define-test test-lambdas-can-have-optional-params +(define-test lambda + ;; A list form starting with the symbol LAMBDA denotes an anonymous function. + ;; It is possible to call that function immediately or to store it for later + ;; use. + (let ((my-function (lambda (a b) (* a b)))) + (assert-equal ____ (funcall my-function 11 9))) + ;; A LAMBDA form is allowed to take the place of a function name. + (assert-equal ____ ((lambda (a b) (+ a b)) 10 9)) + (let ((functions (list (lambda (a b) (+ a b)) + (lambda (a b) (- a b)) + (lambda (a b) (* a b)) + (lambda (a b) (/ a b))))) + (assert-equal ____ (funcall (first functions) 2 33)) + (assert-equal ____ (funcall (second functions) 2 33)) + (assert-equal ____ (funcall (third functions) 2 33)) + (assert-equal ____ (funcall (fourth functions) 2 33)))) + +(define-test lambda-with-optional-parameters (assert-equal ___ ((lambda (a &optional (b 100)) (+ a b)) 10 9)) (assert-equal ___ ((lambda (a &optional (b 100)) (+ a b)) 10))) - - ; returns sign x -(defun sign-of (x) (if (< x 0) (return-from sign-of -1)) (if (eq x 0) (return-from sign-of 0)) 1) - -(define-test test-return-from-function-early - (assert-equal (sign-of -5.5) ___) - (assert-equal (sign-of 0) ___) - (assert-equal (sign-of ___) 1)) - - -;; ---- - - -;; Lambdas create "lexical closures", meaning that the resulting function, when -;; called, will execute in an environment wherein the lexical bindings to all -;; referred to names still apply. -;; This example from "Common Lisp The Language" Ch. 7 - -(defun adder (x) - "The result of (adder n) is a nameless function with one parameter. - This function will add n to its argument." +(defun make-adder (x) + ;; MAKE-ADDER will create a function that closes over the parameter X. + ;; The parameter will be remembered as a part of the environment of the + ;; returned function, which will continue refering to it. (lambda (y) (+ x y))) -(define-test test-lexical-closure-over-adder () - (let ((add-100 (adder 100)) - (add-500 (adder 500))) - "add-100 and add-500 now refer to different bindings to x" - (assert-equal ___ (funcall add-100 3)) - (assert-equal ___ (funcall add-500 3)))) - - -;; ---- - - -;; The closure gives the returned function access to the bindings, not just the -;; values. This means that two functions which close over the same variables -;; will always see the same values of those variables if one does a setq. +(define-test lexical-closures + (let ((adder-100 (make-adder 100)) + (adder-500 (make-adder 500))) + ;; ADD-100 and ADD-500 now close over different values. + (assert-equal ____ (funcall adder-100 3)) + (assert-equal ____ (funcall adder-500 3)))) -(defun two-funs (x) - "Returns a list of two functions. - The first takes no parameters and returns x. - The second takes one parameter, y, and resets x to the value of y." +(defun make-reader-and-writer (x) + ;; Both returned functions will refer to the same place. (list (function (lambda () x)) (function (lambda (y) (setq x y))))) (define-test test-lexical-closure-interactions - "An illustration of how lexical closures may interact." - (let ((tangled-funs-1 (two-funs 1)) - (tangled-funs-2 (two-funs 2))) - (assert-equal (funcall (first tangled-funs-1)) ___) - (funcall (second tangled-funs-1) 0) - (assert-equal (funcall (first tangled-funs-1)) ___) - - (assert-equal (funcall (first tangled-funs-2)) ___) - (funcall (second tangled-funs-2) 100) - (assert-equal (funcall (first tangled-funs-2)) ___))) - - -(define-test test-apply-function-with-apply - "APPLY calls the function parameter on a list of all the remaining - parameters" - (let (f1 f2 f3) - (setq f1 '+) - (setq f2 '-) - (setq f3 'max) - - (assert-equal ___ (apply f1 '(1 2))) - (assert-equal ___ (apply f2 '(1 2))) - - ; after the function name, the parameters are consed onto the front - ; of the very last parameter - (assert-equal ___ (apply f1 1 2 '(3))) - (assert-equal ___ (apply f3 1 2 3 4 '())))) - - -(define-test test-apply-function-with-funcall - "FUNCALL calls the function parameter on a list of all the remaining - parameters. Remaining params do not expect a final list." - (let (f1 f2 f3) - (setq f1 '+) - (setq f2 '-) - (setq f3 'max) - (assert-equal ___ (funcall f1 1 2)) - (assert-equal ___ (funcall f2 1 2)) - (assert-equal ___ (funcall f1 1 2 3)) - (assert-equal ___ (funcall f3 1 2 3 4)))) + ;; The macro DESTRUCTURING-BIND is like LET, except it binds the variables + ;; listed in its first argument to the parts of the list returned by the form + ;; that is its second argument. + (destructuring-bind (reader-1 writer-1) (make-reader-and-writer 1) + (destructuring-bind (reader-2 writer-2) (make-reader-and-writer :one)) + (assert-equal ____ (funcall reader-1)) + (funcall writer-1 0) + (assert-equal ____ (funcall reader-1)) + ;; The two different function pairs refer to different places. + (assert-equal ____ (funcall reader-2)) + (funcall writer-2 :zero) + (assert-equal ____ (funcall reader-2)))) + +(define-test apply + ;; The function APPLY applies a function to a list of arguments. + (let ((function (lambda (x y z) (+ x y z)))) + (assert-equal ____ (apply function '(100 20 3)))) + ;; FUNCTION is a special operator that retrieves function objects, defined + ;; both globally and locally. #'X is syntax sugar for (FUNCTION X). + (assert-equal ____ (apply (function +) '(1 2))) + (assert-equal ____ (apply #'- '(1 2))) + ;; Only the last argument to APPLY must be a list. + (assert-equal ____ (apply #'+ 1 2 '(3))) + (assert-equal ____ (apply #'max 1 2 3 4 '()))) + +(define-test funcall + ;; The function FUNCALL calls a function with arguments, not expecting a final + ;; list of arguments. + (let ((function (lambda (x y z) (+ x y z)))) + (assert-equal ____ (funcall function 300 20 1))) + (assert-equal ____ (funcall (function +) 1 2)) + (assert-equal ____ (funcall #'- 1 2)) + (assert-equal ____ (funcall #'+ 1 2 3)) + (assert-equal ____ (funcall #'max 1 2 3 4))) diff --git a/koans/multiple-values.lisp b/koans/multiple-values.lisp index b98014e9..5459c0ac 100644 --- a/koans/multiple-values.lisp +++ b/koans/multiple-values.lisp @@ -30,7 +30,7 @@ (define-test binding-and-setting-multiple-values ;; The macro MULTIPLE-VALUE-BIND is like LET, except it binds the variables - ;; listed in its first argument to the values returned by the form in its + ;; listed in its first argument to the values returned by the form that is its ;; second argument. (multiple-value-bind (x y) (next-fib 3 5) (let ((result (* x y))) diff --git a/koans/strings.lisp b/koans/strings.lisp index 2baf4676..7fca67bf 100644 --- a/koans/strings.lisp +++ b/koans/strings.lisp @@ -1,78 +1,73 @@ -;; 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. -(define-test test-double-quoted-strings-are-strings - (let ((my-string "do or do not")) - (true-or-false? ___ (typep my-string 'string)) - "strings are the same thing as vectors of characters" - (true-or-false? ___ (typep my-string 'array)) - (assert-equal (aref "meat" 2) (aref "fiesta" 5)) - "strings are not integers :p" - (true-or-false? ___ (typep my-string 'integer)))) +(define-test what-is-a-string + (let ((string "Do, or do not. There is no try.")) + (true-or-false? ____ (typep string 'string)) + ;; Strings are vectors of characters. + (true-or-false? ____ (typep string 'array)) + (true-or-false? ____ (typep string 'vector)) + (true-or-false? ____ (typep string '(vector character))) + (true-or-false? ____ (typep string 'integer)))) +(define-test multiline-string + ;; A Lisp string can span multiple lines. + (let ((string "this is + a multi + line string")) + (true-or-false? ___ (typep string 'string)))) -(define-test test-multi-line-strings-are-strings - (let ((my-string "this is - a multi - line string")) - (true-or-false? ___ (typep my-string 'string)))) +(define-test escapes-in-strings + ;; Quotes and backslashes in Lisp strings must be escaped. + (let ((my-string "this string has one of these \" and a \\ in it")) + (true-or-false? ____ (typep my-string 'string)))) +(define-test substrings + ;; Since strings are sequences, it is possible to use SUBSEQ on them. + (let ((string "Lorem ipsum dolor sit amet")) + (assert-equal ____ (subseq string 12)) + (assert-equal ____ (subseq string 6 11)) + (assert-equal ____ (subseq string 1 5)))) -(define-test test-escape-quotes - (let ((my-string "this string has one of these \" in it")) - (true-or-false? ___ (typep my-string 'string)))) - - -; This test from common lisp cookbook -(define-test test-substrings - "since strings are sequences, you may use subseq" - (let ((my-string "Groucho Marx")) - (assert-equal "Marx" (subseq my-string 8)) - (assert-equal (subseq my-string 0 7) ____) - (assert-equal (subseq my-string 1 5) ____))) - -(define-test test-accessing-individual-characters - "char literals look like this" - (true-or-false? ___ (typep #\a 'character)) - (true-or-false? ___ (typep "A" 'character)) - (true-or-false? ___ (typep #\a 'string)) - "char is used to access individual characters" +(define-test strings-versus-characters + ;; Strings and characters have distinct types. + (true-or-false? ____ (typep #\a 'character)) + (true-or-false? ____ (typep "A" 'character)) + (true-or-false? ____ (typep #\a 'string)) + ;; One can use both AREF and CHAR to refer to characters in a string. (let ((my-string "Cookie Monster")) - (assert-equal (char my-string 0) #\C) - (assert-equal (char my-string 3) #\k) - (assert-equal (char my-string 7) ___))) - + (assert-equal ____ (char my-string 0)) + (assert-equal ____ (char my-string 3)) + (assert-equal ____ (aref my-string 7)))) (define-test test-concatenating-strings - "concatenating strings in lisp is a little cumbersome" - (let ((a "this") - (b "is") - (c "unwieldy")) - (assert-equal ___ (concatenate 'string a " " b " " c)))) - + ;; Concatenating strings in Common Lisp is possible, if a little cumbersome. + (let ((a "Lorem") + (b "ipsum") + (c "dolor")) + (assert-equal ____ (concatenate 'string a " " b " " c)))) (define-test test-searching-for-characters - "you can use position to detect characters in strings - (or elements of sequences)" - (assert-equal ___ (position #\b "abc")) - (assert-equal ___ (position #\c "abc")) - (assert-equal ___ (find #\d "abc"))) - + ;; The function POSITION can be used to find the first position of an element + ;; in a sequence. If the element is not found, NIL is returned. + (assert-equal ____ (position #\b "abc")) + (assert-equal ____ (position #\c "abc")) + (assert-equal ____ (position #\d "abc"))) (define-test test-finding-substrings - "search finds subsequences" + ;; The function SEARCH can be used to search a sequence for subsequences. (let ((title "A supposedly fun thing I'll never do again")) - (assert-equal 2 (search "supposedly" title)) - (assert-equal 12 (search "CHANGETHISWORD" title)))) + (assert-equal ____ (search "supposedly" title)) + (assert-equal 12 (search ____ title))))