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», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.
Интервал:
Закладка:
This is a leak in the abstraction because, to use the macro correctly, the caller needs to be aware that the end
form is going to be evaluated more than once. One way to plug this leak would be to simply define this as the behavior of do-primes
. But that's not very satisfactory—you should try to observe the Principle of Least Astonishment when implementing macros. And programmers will typically expect the forms they pass to macros to be evaluated no more times than absolutely necessary. [98] Of course, certain forms are supposed to be evaluated more than once, such as the forms in the body of a do-primes loop.
Furthermore, since do-primes
is built on the model of the standard macros, DOTIMES
and DOLIST
, neither of which causes any of the forms except those in the body to be evaluated more than once, most programmers will expect do-primes
to behave similarly.
You can fix the multiple evaluation easily enough; you just need to generate code that evaluates end
once and saves the value in a variable to be used later. Recall that in a DO
loop, variables defined with an initialization form and no step form don't change from iteration to iteration. So you can fix the multiple evaluation problem with this definition:
(defmacro do-primes ((var start end) &body body)
`(do ((ending-value ,end)
(,var (next-prime ,start) (next-prime (1+ ,var))))
((> ,var ending-value))
,@body))
Unfortunately, this fix introduces two new leaks to the macro abstraction.
One new leak is similar to the multiple-evaluation leak you just fixed. Because the initialization forms for variables in a DO
loop are evaluated in the order the variables are defined, when the macro expansion is evaluated, the expression passed as end
will be evaluated before the expression passed as start
, opposite to the order they appear in the macro call. This leak doesn't cause any problems when start
and end
are literal values like 0 and 19. But when they're forms that can have side effects, evaluating them out of order can once again run afoul of the Principle of Least Astonishment.
This leak is trivially plugged by swapping the order of the two variable definitions.
(defmacro do-primes ((var start end) &body body)
`(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
(ending-value ,end))
((> ,var ending-value))
,@body))
The last leak you need to plug was created by using the variable name ending-value
. The problem is that the name, which ought to be a purely internal detail of the macro implementation, can end up interacting with code passed to the macro or in the context where the macro is called. The following seemingly innocent call to do-primes
doesn't work correctly because of this leak:
(do-primes (ending-value 0 10)
(print ending-value))
Neither does this one:
(let ((ending-value 0))
(do-primes (p 0 10)
(incf ending-value p))
ending-value)
Again, MACROEXPAND-1
can show you the problem. The first call expands to this:
(do ((ending-value (next-prime 0) (next-prime (1+ ending-value)))
(ending-value 10))
((> ending-value ending-value))
(print ending-value))
Some Lisps may reject this code because ending-value
is used twice as a variable name in the same DO
loop. If not rejected outright, the code will loop forever since ending-value
will never be greater than itself.
The second problem call expands to the following:
(let ((ending-value 0))
(do ((p (next-prime 0) (next-prime (1+ p)))
(ending-value 10))
((> p ending-value))
(incf ending-value p))
ending-value)
In this case the generated code is perfectly legal, but the behavior isn't at all what you want. Because the binding of ending-value
established by the LET
outside the loop is shadowed by the variable with the same name inside the DO
, the form (incf ending-value p)
increments the loop variable ending-value
instead of the outer variable with the same name, creating another infinite loop. [99] It may not be obvious that this loop is necessarily infinite given the nonuniform occurrences of prime numbers. The starting point for a proof that it is in fact infinite is Bertrand's postulate, which says for any n > 1, there exists a prime p , n < p < 2n . From there you can prove that for any prime number, P less than the sum of the preceding prime numbers, the next prime, P', is also smaller than the original sum plus P.
Clearly, what you need to patch this leak is a symbol that will never be used outside the code generated by the macro. You could try using a really unlikely name, but that's no guarantee. You could also protect yourself to some extent by using packages, as described in Chapter 21. But there's a better solution.
The function GENSYM
returns a unique symbol each time it's called. This is a symbol that has never been read by the Lisp reader and never will be because it isn't interned in any package. Thus, instead of using a literal name like ending-value
, you can generate a new symbol each time do-primes
is expanded.
(defmacro do-primes ((var start end) &body body)
(let ((ending-value-name (gensym)))
`(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
(,ending-value-name ,end))
((> ,var ,ending-value-name))
,@body)))
Note that the code that calls GENSYM
isn't part of the expansion; it runs as part of the macro expander and thus creates a new symbol each time the macro is expanded. This may seem a bit strange at first— ending-value-name
is a variable whose value is the name of another variable. But really it's no different from the parameter var
whose value is the name of a variable—the difference is the value of var
was created by the reader when the macro form was read, and the value of ending-value-name
is generated programmatically when the macro code runs.
With this definition the two previously problematic forms expand into code that works the way you want. The first form:
(do-primes (ending-value 0 10)
(print ending-value))
expands into the following:
(do ((ending-value (next-prime 0) (next-prime (1+ ending-value)))
(#:g2141 10))
((> ending-value #:g2141))
(print ending-value))
Now the variable used to hold the ending value is the gensymed symbol, #:g2141
. The name of the symbol, G2141
, was generated by GENSYM
but isn't significant; the thing that matters is the object identity of the symbol. Gensymed symbols are printed in the normal syntax for uninterned symbols, with a leading #:
.
The other previously problematic form:
(let ((ending-value 0))
(do-primes (p 0 10)
(incf ending-value p))
Интервал:
Закладка:
Похожие книги на «Practical Common Lisp»
Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.
Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.