Peter Siebel - Practical Common Lisp
Здесь есть возможность читать онлайн «Peter Siebel - Practical Common Lisp» весь текст электронной книги совершенно бесплатно (целиком полную версию без сокращений). В некоторых случаях можно слушать аудио, скачать через торрент в формате fb2 и присутствует краткое содержание. Год выпуска: 2005, ISBN: 2005, Издательство: Apress, Жанр: Программирование, на английском языке. Описание произведения, (предисловие) а так же отзывы посетителей доступны на портале библиотеки ЛибКат.
- Название:Practical Common Lisp
- Автор:
- Издательство:Apress
- Жанр:
- Год:2005
- ISBN:1-59059-239-5
- Рейтинг книги:4 / 5. Голосов: 1
-
Избранное:Добавить в избранное
- Отзывы:
-
Ваша оценка:
- 80
- 1
- 2
- 3
- 4
- 5
Practical Common Lisp: краткое содержание, описание и аннотация
Предлагаем к чтению аннотацию, описание, краткое содержание или предисловие (зависит от того, что написал сам автор книги «Practical Common Lisp»). Если вы не нашли необходимую информацию о книге — напишите в комментариях, мы постараемся отыскать её.
Practical Common Lisp — читать онлайн бесплатно полную книгу (весь текст) целиком
Ниже представлен текст книги, разбитый по страницам. Система сохранения места последней прочитанной страницы, позволяет с удобством читать онлайн бесплатно книгу «Practical Common Lisp», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.
Интервал:
Закладка:
The backwards
macro thus defines a new language that's a lot like Lisp—just backward—that you can drop into anytime simply by wrapping a backward Lisp expression in a call to the backwards
macro. And, in a compiled Lisp program, that new language is just as efficient as normal Lisp because all the macro code—the code that generates the new expression—runs at compile time. In other words, the compiler will generate exactly the same code whether you write (backwards ("hello, world" t format))
or (format t "hello, world")
.
So how does that help with the code duplication in where
? Well, you can write a macro that generates exactly the code you need for each particular call to where
. Again, the best approach is to build our code bottom up. In the hand-optimized selector function, you had an expression of the following form for each actual field referred to in the original call to where
:
(equal (getf cd field ) value )
So let's write a function that, given the name of a field and a value, returns such an expression. Since an expression is just a list, you might think you could write something like this:
(defun make-comparison-expr (field value) ; wrong
(list equal (list getf cd field) value))
However, there's one trick here: as you know, when Lisp sees a simple name such as field
or value
other than as the first element of a list, it assumes it's the name of a variable and looks up its value. That's fine for field
and value
; it's exactly what you want. But it will treat equal
, getf
, and cd
the same way, which isn't what you want. However, you also know how to stop Lisp from evaluating a form: stick a single forward quote ( '
) in front of it. So if you write make-comparison-expr
like this, it will do what you want:
(defun make-comparison-expr (field value)
(list 'equal (list 'getf 'cd field) value))
You can test it out in the REPL.
CL-USER> (make-comparison-expr :rating 10)
(EQUAL (GETF CD :RATING) 10)
CL-USER> (make-comparison-expr :title "Give Us a Break")
(EQUAL (GETF CD :TITLE) "Give Us a Break")
It turns out that there's an even better way to do it. What you'd really like is a way to write an expression that's mostly not evaluated and then have some way to pick out a few expressions that you do want evaluated. And, of course, there's just such a mechanism. A back quote ( `
) before an expression stops evaluation just like a forward quote.
CL-USER> `(1 2 3)
(1 2 3)
CL-USER> '(1 2 3)
(1 2 3)
However, in a back-quoted expression, any subexpression that's preceded by a comma is evaluated. Notice the effect of the comma in the second expression:
`(1 2 (+ 1 2)) ==> (1 2 (+ 1 2))
`(1 2 ,(+ 1 2)) ==> (1 2 3)
Using a back quote, you can write make-comparison-expr
like this:
(defun make-comparison-expr (field value)
`(equal (getf cd ,field) ,value))
Now if you look back to the hand-optimized selector function, you can see that the body of the function consisted of one comparison expression per field/value pair, all wrapped in an AND
expression. Assume for the moment that you'll arrange for the arguments to the where
macro to be passed as a single list. You'll need a function that can take the elements of such a list pairwise and collect the results of calling make-comparison-expr
on each pair. To implement that function, you can dip into the bag of advanced Lisp tricks and pull out the mighty and powerful LOOP
macro.
(defun make-comparisons-list (fields)
(loop while fields
collecting (make-comparison-expr (pop fields) (pop fields))))
A full discussion of LOOP
will have to wait until Chapter 22; for now just note that this LOOP
expression does exactly what you need: it loops while there are elements left in the fields
list, popping off two at a time, passing them to make-comparison-expr
, and collecting the results to be returned at the end of the loop. The POP
macro performs the inverse operation of the PUSH
macro you used to add records to *db*
.
Now you just need to wrap up the list returned by make-comparison-list
in an AND
and an anonymous function, which you can do in the where
macro itself. Using a back quote to make a template that you fill in by interpolating the value of make-comparisons-list
, it's trivial.
(defmacro where (&rest clauses)
`#'(lambda (cd) (and ,@(make-comparisons-list clauses))))
This macro uses a variant of ,
(namely, the ,@
) before the call to make-comparisons-list
. The ,@
"splices" the value of the following expression—which must evaluate to a list—into the enclosing list. You can see the difference between ,
and ,@
in the following two expressions:
`(and ,(list 1 2 3)) ==> (AND (1 2 3))
`(and ,@(list 1 2 3)) ==> (AND 1 2 3)
You can also use ,@
to splice into the middle of a list.
`(and ,@(list 1 2 3) 4) ==> (AND 1 2 3 4)
The other important feature of the where
macro is the use of &rest
in the argument list. Like &key
, &rest
modifies the way arguments are parsed. With a &rest
in its parameter list, a function or macro can take an arbitrary number of arguments, which are collected into a single list that becomes the value of the variable whose name follows the &rest
. So if you call where
like this:
(where :title "Give Us a Break" :ripped t)
the variable clauses
will contain the list.
(:title "Give Us a Break" :ripped t)
This list is passed to make-comparisons-list
, which returns a list of comparison expressions. You can see exactly what code a call to where
will generate using the function MACROEXPAND-1
. If you pass MACROEXPAND-1
, a form representing a macro call, it will call the macro code with appropriate arguments and return the expansion. So you can check out the previous where
call like this:
CL-USER> (macroexpand-1 '(where :title "Give Us a Break" :ripped t))
#'(LAMBDA (CD)
(AND (EQUAL (GETF CD :TITLE) "Give Us a Break")
(EQUAL (GETF CD :RIPPED) T)))
T
Looks good. Let's try it for real.
CL-USER> (select (where :title "Give Us a Break" :ripped t))
((:TITLE "Give Us a Break" :ARTIST "Limpopo" :RATING 10 :RIPPED T))
It works. And the where
macro with its two helper functions is actually one line shorter than the old where
function. And it's more general in that it's no longer tied to the specific fields in our CD records.
Wrapping Up
Now, an interesting thing has happened. You removed duplication and made the code more efficient and more general at the same time. That's often the way it goes with a well-chosen macro. This makes sense because a macro is just another mechanism for creating abstractions—abstraction at the syntactic level, and abstractions are by definition more concise ways of expressing underlying generalities. Now the only code in the mini-database that's specific to CDs and the fields in them is in the make-cd
, prompt-for-cd
, and add-cd
functions. In fact, our new where
macro would work with any plist-based database.
Интервал:
Закладка:
Похожие книги на «Practical Common Lisp»
Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.
Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.