Peter Siebel - Practical Common Lisp

Здесь есть возможность читать онлайн «Peter Siebel - Practical Common Lisp» весь текст электронной книги совершенно бесплатно (целиком полную версию без сокращений). В некоторых случаях можно слушать аудио, скачать через торрент в формате fb2 и присутствует краткое содержание. Год выпуска: 2005, ISBN: 2005, Издательство: Apress, Жанр: Программирование, на английском языке. Описание произведения, (предисловие) а так же отзывы посетителей доступны на портале библиотеки ЛибКат.

Practical Common Lisp: краткое содержание, описание и аннотация

Предлагаем к чтению аннотацию, описание, краткое содержание или предисловие (зависит от того, что написал сам автор книги «Practical Common Lisp»). Если вы не нашли необходимую информацию о книге — напишите в комментариях, мы постараемся отыскать её.

Practical Common Lisp — читать онлайн бесплатно полную книгу (весь текст) целиком

Ниже представлен текст книги, разбитый по страницам. Система сохранения места последней прочитанной страницы, позволяет с удобством читать онлайн бесплатно книгу «Practical Common Lisp», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.

Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

The backwardsmacro 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 backwardsmacro. 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 fieldor valueother 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 fieldand value; it's exactly what you want. But it will treat equal, getf, and cdthe 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-exprlike 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-exprlike 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 wheremacro 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-expron each pair. To implement that function, you can dip into the bag of advanced Lisp tricks and pull out the mighty and powerful LOOPmacro.

(defun make-comparisons-list (fields)

(loop while fields

collecting (make-comparison-expr (pop fields) (pop fields))))

A full discussion of LOOPwill have to wait until Chapter 22; for now just note that this LOOPexpression does exactly what you need: it loops while there are elements left in the fieldslist, 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 PUSHmacro you used to add records to *db*.

Now you just need to wrap up the list returned by make-comparison-listin an ANDand an anonymous function, which you can do in the wheremacro 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 wheremacro is the use of &restin the argument list. Like &key, &restmodifies the way arguments are parsed. With a &restin 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 wherelike this:

(where :title "Give Us a Break" :ripped t)

the variable clauseswill 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 wherewill 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 wherecall 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 wheremacro with its two helper functions is actually one line shorter than the old wherefunction. 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-cdfunctions. In fact, our new wheremacro would work with any plist-based database.

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

Похожие книги на «Practical Common Lisp»

Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.


Отзывы о книге «Practical Common Lisp»

Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.

x