PRACTICAL COMMON LISP Peter Seibel 1.

Slides:



Advertisements
Similar presentations
Test-First Programming. The tests should drive you to write the code, the reason you write code is to get a test to succeed, and you should only write.
Advertisements

Some non-recursive tricks. The Lambda expression. More on Let, Let*, apply and funcall.
ANSI Common Lisp 5. Control 16 June Blocks -- progn progn > (progn (format t “a”) (format t “b”) ( )) ab 23 The expressions within its body.
A problem with functions: arguments must always have values that can be worked out Whenever we call a function we give it arguments. Lisp then works out.
10 Macros.  Lisp code is expressed as lists, which are Lisp objects  This makes it possible to write programs that would write programs  This lecture.
Generics "It is easy to study the rules of overloading and of templates without noticing that together they are one of the keys to elegant and efficient.
Mrs. Chapman. Tabs (Block Categories) Commands Available to use Script Area where you type your code Sprite Stage All sprites in this project.
ITERATIVE CONSTRUCTS: DOLIST Dolist is an iterative construct (a loop statement) consisting of a variable declaration and a body The body states what happens.
Imperative programming public int factorial (int N){ int F = 1; for(X=N; X>1; X-- ){ F= F*X; } return F; } Functional programming (defun factorial(N) (cond.
Introduction to Computers and Programming Lecture 15: Arrays Professor: Evan Korth New York University.
>(setf oldlist ) Constructing a list We know how to make a list in lisp; we simply write it: ‘4321( ) What if we want a new list with that list as a part?
CSE S. Tanimoto Macros 1 Defining Macros in Lisp Extensibility: A language is extensible if the language can be extended. New Lisp control structures.
28-Jun-15 Recognizers. 2 Parsers and recognizers Given a grammar (say, in BNF) and a string, A recognizer will tell whether the string belongs to the.
PRACTICAL COMMON LISP Peter Seibel 1.
Recursion. Definitions I A recursive definition is a definition in which the thing being defined occurs as part of its own definition Example: A list.
Functional Programming COMP2003 A course on functional programming using Common Lisp Dr Eleni Mangina
General pattern for selecting some elements of a list This negatives example illustrates a general pattern: If you want a function which selects some elements.
Mr. Wortzman. Tabs (Block Categories) Available Blocks Script Area Sprite Stage All sprites in this project.
PRACTICAL COMMON LISP Peter Seibel 1.
PRACTICAL COMMON LISP Peter Seibel 1.
Common Lisp Macros Read for Input Macros Macro lifetime Macro syntax
1 Lisp Functions –Built-in functions –Defining functions –Function Evaluation and Special Forms defun, if Control statements –Conditional if, cond –Repetition.
Functional Programming and Lisp. Overview In a functional programming language, functions are first class objects. In a functional programming language,
What is an Array? An array is a collection of variables. Arrays have three important properties: –group of related items(for example, temperature for.
04 Control. Control-Blocks Common Lisp has 3 basic operators for creating blocks of code progn block tagbody If ordinary function calls are the leaves.
Improving the Quality of Existing Code Svetlin Nakov Telerik Corporation
The Loop Macro Many of the CL “functions” are actually macros (let, progn, if, etc) The most complicated macro in CL is probably the Loop macro –The Loop.
Lecture 1-2CS251: Intro to AI/Lisp II “And now for something completely different…”
Lecture 6-2CS250: Intro to AI/Lisp Programming in Your Favorite Language Lecture 5-2 February 11 th, 1999 CS250.
PRACTICAL COMMON LISP Peter Seibel 1.
Function Design in LISP. Program Files n LISP programs are plain text –DOS extensions vary; use.lsp for this course n (load “filename.lsp”) n Can use.
Macros “How can you get anything done in [other languages], I think, without macros?” - Paul Graham, 2003.
Python uses boolean variables to evaluate conditions. The boolean values True and False are returned when an expression is compared or evaluated.
CSE S. Tanimoto Lisp Defining Macros in Lisp Extensibility: A language is extensible if the language can be extended. New Lisp control structures.
Introduction to LISP. Lisp Extensible: It lets you define new operators yourself Lisp programs are expressed as lisp data structures –You can write programs.
PRACTICAL COMMON LISP Peter Seibel 1.
UMBC CMSC Common Lisp II. UMBC CMSC Input and Output Print is the most primitive output function > (print (list 'foo 'bar)) (FOO BAR) The.
PRACTICAL COMMON LISP Peter Seibel 1.
Design of Problem Solvers (PS) using Classical Problem Solving (CPS) techniques Classical Problem Solver has 2 basic components:  Search engine (uses.
PRACTICAL COMMON LISP Peter Seibel 1.
Refactoring1 Improving the structure of existing code.
1 COSC generating functions, templates, and macros Yves Lespérance Adapted from Peter Roosen-Runge.
Basic Introduction to Lisp
Macros and general code walkers in Lisp: how useful! or, how useful? Ernst van Waning
CSE 143 Lecture 13 Recursive Backtracking slides created by Ethan Apter
PRACTICAL COMMON LISP Peter Seibel 1.
LECTURE 2 Python Basics. MODULES So, we just put together our first real Python program. Let’s say we store this program in a file called fib.py. We have.
Unit – 3 Control structures. Condition Statements 1.If.…..else :- Has someone ever told you, "if you work hard, then you will succeed"? And what happens.
Macros CSC 358/ Outline  Homework #6  Macro Intro  Backquote  Macros  Modify macros  Read Macros.
11 Making Decisions in a Program Session 2.3. Session Overview  Introduce the idea of an algorithm  Show how a program can make logical decisions based.
Clojure Macros. Homoiconicity All versions of Lisp, including Clojure, are homoiconic This means that there is no difference between the form of the data.
Macros Forms that the compiler expands into code ● Decide if the macro is really necessary ● Write down the syntax of the macro ● Figure out what the macro.
CS 5010 Program Design Paradigms “Bootcamp” Lesson 2.4
Functions CSC 358/
Defining Macros in Lisp
CSE341: Programming Languages Lecture 15 Macros
Getting Started with Lisp
PROGRAMMING IN HASKELL
CSE341: Programming Languages Lecture 15 Macros
Conditions and Ifs BIS1523 – Lecture 8.
PROGRAMMING IN HASKELL
CSE341: Programming Languages Lecture 15 Macros
Defining Macros in Lisp
CSE341: Programming Languages Lecture 15 Macros
Recursion Taken from notes by Dr. Neil Moore
Clojure Macros.
Peter Seibel Practical Common Lisp Peter Seibel
CSE341: Programming Languages Lecture 15 Macros
Common Lisp II.
CSE341: Programming Languages Lecture 15 Macros
Presentation transcript:

PRACTICAL COMMON LISP Peter Seibel 1

CHAPTER 9 PRACTICAL: BUILDING A UNIT TEST FRAMEWORK 2

TWO FIRST TRIES The key feature of an automated testing framework is that the framework is responsible for telling you whether all the tests passed. Each test case must be an expression that yields a boolean value—true or false, pass or fail. For instance, if you were writing tests for the built-in + function, these might be reasonable test cases: (= (+ 1 2) 3) (= ( ) 6) (= ( ) -4) Now, we can just write a function that evaluates the test cases and ANDs the results together. (defun test-+ () (and (= (+ 1 2) 3) (= ( ) 6) (= ( ) -4))) Whenever you want to run this set of test cases, you can call test-+. CL-USER> (test-+) T 3

TWO FIRST TRIES However, once a test case fails, we'll know something failed, but we'll have no idea which test case it was. So let's try another simple approach to find out what happens to each test case. (defun test-+ () (format t "~:[FAIL~;pass~]... ~a~%" (= (+ 1 2) 3) '(= (+ 1 2) 3)) (format t "~:[FAIL~;pass~]... ~a~%" (= ( ) 6) '(= ( ) 6)) (format t "~:[FAIL~;pass~]... ~a~%" (= ( ) -4) '(= ( ) -5))) The ~:[FAIL~;pass~] part of the FORMAT directive causes FORMAT to print “FAIL” if the first format argument is false and “pass” otherwise. CL-USER> (test-+) pass... (= (+ 1 2) 3) pass... (= ( ) 6) pass... (= ( ) -4) NIL 4

REFACTORING The repeated calls to FORMAT should be refactored( 重構 ). The simplest way to get rid of the repeated similar calls to FORMAT is to create a new function. (defun report-result (result form) (format t "~:[FAIL~;pass~]... ~a~%" result form)) Now we can write test-+ with calls to report-result instead of FORMAT. (defun test-+ () (report-result (= (+ 1 2) 3) '(= (+ 1 2) 3)) (report-result (= ( ) 6) '(= ( ) 6)) (report-result (= ( ) -4) '(= ( ) -4))) Refactoring( 重構代碼 ): 指在不改變代碼的外部行為情況下修改原始碼 5

REFACTORING Next we need to get rid of the duplication of the test case expression, with its attendant risk of mislabeling of results. (defmacro check (form) ` (report-result,form ',form)) For example, (check (= (+ 1 2) 3)) means (report-result (= (+ 1 2) 3) '(= (+ 1 2) 3)) Now we can change test-+ to use check. (defun test-+ () (check (= (+ 1 2) 3)) (check (= ( ) 6)) (check (= ( ) -4))) 6

REFACTORING We can define check to take an arbitrary number of forms and wrap them each in a call to report-result. (defmacro check (&body forms) for f in forms collect `(report-result,f ',f)))) This definition uses a common macro idiom of wrapping a PROGN around a series of forms in order to turn them into a single form. to splice in the result of an expression that returns a list of expressions that are themselves generated with a backquote template. With the new version of check we can write a new version of test-+ like this: (defun test-+ () (check (= (+ 1 2) 3) (= ( ) 6) (= ( ) -4))) 7 > (test-+) pass... (= (+ 1 2) 3) pass... (= ( ) 6) pass... (= ( ) -4) NIL

FIXING THE RETURN VALUE As a first step, you can make a small change to report-result so it returns the result of the test case it's reporting. (defun report-result (result form) (format t "~:[FAIL~;pass~]... ~a~%" result form) result) > (test-+) pass... (= (+ 1 2) 3) pass... (= ( ) 6) pass... (= ( ) -4) T 8

FIXING THE RETURN VALUE We can define a macro combine-results which can work like this: (combine-results (foo) (bar) (baz)) It means that (let ((result t)) (unless (foo) (setf result nil)) (unless (bar) (setf result nil)) (unless (baz) (setf result nil)) result) Now, combine-results can be defined as: (defmacro combine-results (&body forms) (with-gensyms (result) `(let ((,result for f in forms collect `(unless,f (setf,result nil))),result))) Ps. with-gensyms: 9

FIXING THE RETURN VALUE We can fix check by simply changing the expansion to use combine- results instead of PROGN. (defmacro check (&body forms) for f in forms collect `(report-result,f ',f)))) CL-USER> (test-+) pass... (= (+ 1 2) 3) pass... (= ( ) 6) pass... (= ( ) -4) T And if you change one of the test cases so it fails, the final return value changes to NIL. CL-USER> (test-+) pass... (= (+ 1 2) 3) pass... (= ( ) 6) FAIL... (= ( ) -5) NIL 10

BETTER RESULT REPORTING If we write a lot of tests, we’ll probably want to organize them somehow, rather than shoving( 亂塞 ) them all into one function. For instance, suppose we wanted to add some test cases for the * function. We might write a new test function. (defun test-* () (check (= (* 2 2) 4) (= (* 3 5) 15))) Now that you have two test functions, you’ll probably want another function that runs all the tests. (defun test-arithmetic () (progn (test-+) (test-*))) 11

BETTER RESULT REPORTING CL-USER> (test-arithmetic) pass... (= (+ 1 2) 3) pass... (= ( ) 6) pass... (= ( ) -4) pass... (= (* 2 2) 4) pass... (= (* 3 5) 15) T Now imagine that one of the test cases failed and we need to track down the problem. With only five test cases and two test functions, it won’t be too hard to find the code of the failing test case. But suppose we had 500 test cases spread across 20 functions. It might be nice if the results told us what function each test case came from. 12

BETTER RESULT REPORTING (defvar *test-name* nil) (defun report-result (result form) (format t "~:[FAIL~;pass~]... ~a: ~a~%" result *test-name* form) result) (defmacro check (&body forms) for f in forms collect `(report-result,f ',f)))) (defun test-+ () (let ((*test-name* 'test-+)) (check (= (+ 1 2) 3) (= ( ) 6) (= ( ) -4)))) (defun test-* () (let ((*test-name* 'test-*)) (check (= (* 2 2) 4) (= (* 3 5) 15))) (defun test-arithmetic () (progn (test-+) (test-*)))) 13 CL-USER> (test-arithmetic) pass... TEST-+: (= (+ 1 2) 3) pass... TEST-+: (= ( ) 6) pass... TEST-+: (= ( ) -4) pass... TEST-*: (= (* 2 2) 4) pass... TEST-*: (= (* 3 5) 15) T