common-lisp-study/lisp-koans/koans-solved/functions.lisp

185 lines
8.0 KiB
Common Lisp

;;; 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.
(defun some-named-function (a b)
(+ a b))
(define-test call-a-function
;; DEFUN can be used to define global functions.
(assert-equal 9 (some-named-function 4 5))
;; FLET can be used to define local functions.
(flet ((another-named-function (a b) (* a b)))
(assert-equal 20 (another-named-function 4 5)))
;; LABELS can be used to define local functions which can refer to themselves
;; or each other.
(labels ((recursive-function (a b)
(if (or (= 0 a) (= 0 b))
1
(+ (* a b) (recursive-function (1- a) (1- b))))))
(assert-equal 41 (recursive-function 4 5))))
(define-test shadow-a-function
(assert-eq 18 (some-named-function 7 11))
;; FLET and LABELS can shadow function definitions.
(flet ((some-named-function (a b) (* a b)))
(assert-equal 77 (some-named-function 7 11)))
(assert-equal 18 (some-named-function 7 11)))
(defun function-with-optional-parameters (&optional (a 2) (b 3) c)
;; If an optional argument to a function is not provided, it is given its
;; default value, or NIL, if no default value is specified.
(list a b c))
(define-test optional-parameters
(assert-equal '(42 24 4224) (function-with-optional-parameters 42 24 4224))
(assert-equal '(42 24 nil) (function-with-optional-parameters 42 24))
(assert-equal '(42 3 nil) (function-with-optional-parameters 42))
(assert-equal '(2 3 nil) (function-with-optional-parameters)))
(defun function-with-optional-indication
(&optional (a 2 a-provided-p) (b 3 b-provided-p))
;; It is possible to check whether an optional argument was provided.
(list a a-provided-p b b-provided-p))
(define-test optional-indication
(assert-equal '(42 t 24 t) (function-with-optional-indication 42 24))
(assert-equal '(42 t 3 nil) (function-with-optional-indication 42))
(assert-equal '(2 nil 3 nil) (function-with-optional-indication)))
(defun function-with-rest-parameter (&rest x)
;; A rest parameter gathers all remaining parameters in a list.
x)
(define-test rest-parameter
(assert-equal '() (function-with-rest-parameter))
(assert-equal '(1) (function-with-rest-parameter 1))
(assert-equal '(1 :two 333) (function-with-rest-parameter 1 :two 333)))
(defun function-with-keyword-parameters (&key (a :something) b c)
;; A keyword parameters is similar to an optional parameter, but is provided
;; by a keyword-value pair.
(list a b c))
(define-test keyword-parameters ()
(assert-equal '(:something nil nil) (function-with-keyword-parameters))
(assert-equal '(11 22 33) (function-with-keyword-parameters :a 11 :b 22 :c 33))
;; It is not necessary to specify all keyword parameters.
(assert-equal '(:something 22 nil) (function-with-keyword-parameters :b 22))
;; Keyword argument order is not important.
(assert-equal '(0 22 -5/2)
(function-with-keyword-parameters :b 22 :c -5/2 :a 0))
;; Lisp handles duplicate keyword parameters.
(assert-equal '(:something 22 nil)
(function-with-keyword-parameters :b 22 :b 40 :b 812)))
(defun function-with-keyword-indication
(&key (a 2 a-provided-p) (b 3 b-provided-p))
;; It is possible to check whether a keyword argument was provided.
(list a a-provided-p b b-provided-p))
(define-test keyword-indication
(assert-equal '(2 nil 3 nil) (function-with-keyword-indication))
(assert-equal '(3 t 4 t) (function-with-keyword-indication :a 3 :b 4))
(assert-equal '(11 t 22 t) (function-with-keyword-indication :a 11 :b 22))
(assert-equal '(2 nil 22 t) (function-with-keyword-indication :b 22))
(assert-equal '(0 t 22 t) (function-with-keyword-indication :b 22 :a 0)))
(defun function-with-funky-parameters (a &rest x &key b (c a c-provided-p))
;; Lisp functions can have surprisingly complex lambda lists.
;; A &rest parameter must come before &key parameters.
(list a b c c-provided-p x))
(define-test funky-parameters
(assert-equal '(1 nil 1 nil nil) (function-with-funky-parameters 1))
(assert-equal '(1 2 1 nil (:b 2)) (function-with-funky-parameters 1 :b 2))
(assert-equal '(1 2 3 t (:b 2 :c 3))
(function-with-funky-parameters 1 :b 2 :c 3))
(assert-equal '(1 2 3 t (:c 3 :b 2))
(function-with-funky-parameters 1 :c 3 :b 2)))
(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 99 (funcall my-function 11 9)))
;; A LAMBDA form is allowed to take the place of a function name.
(assert-equal 19 ((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 35 (funcall (first functions) 2 33))
(assert-equal -31 (funcall (second functions) 2 33))
(assert-equal 66 (funcall (third functions) 2 33))
(assert-equal 2/33 (funcall (fourth functions) 2 33))))
(define-test lambda-with-optional-parameters
(assert-equal 19 ((lambda (a &optional (b 100)) (+ a b)) 10 9))
(assert-equal 110 ((lambda (a &optional (b 100)) (+ a b)) 10)))
(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 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 103 (funcall adder-100 3))
(assert-equal 503 (funcall adder-500 3))))
(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 lexical-closure-interactions
;; 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 1 (funcall reader-1))
(funcall writer-1 0)
(assert-equal 0 (funcall reader-1))
;; The two different function pairs refer to different places.
(assert-equal :one (funcall reader-2))
(funcall writer-2 :zero)
(assert-equal :zero (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 123 (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 3 (apply (function +) '(1 2)))
(assert-equal -1 (apply #'- '(1 2)))
;; Only the last argument to APPLY must be a list.
(assert-equal 6 (apply #'+ 1 2 '(3)))
(assert-equal 4 (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 321 (funcall function 300 20 1)))
(assert-equal 3 (funcall (function +) 1 2))
(assert-equal -1 (funcall #'- 1 2))
(assert-equal 6 (funcall #'+ 1 2 3))
(assert-equal 4 (funcall #'max 1 2 3 4)))