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», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.

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

Интервал:

Закладка:

Сделать

ending-value)

looks like this if you replace the do-primesform with its expansion:

(let ((ending-value 0))

(do ((p (next-prime 0) (next-prime (1+ p)))

(#:g2140 10))

((> p #:g2140))

(incf ending-value p))

ending-value)

Again, there's no leak since the ending-valuevariable bound by the LET surrounding the do-primesloop is no longer shadowed by any variables introduced in the expanded code.

Not all literal names used in a macro expansion will necessarily cause a problem—as you get more experience with the various binding forms, you'll be able to determine whether a given name is being used in a position that could cause a leak in a macro abstraction. But there's no real downside to using a gensymed name just to be safe.

With that fix, you've plugged all the leaks in the implementation of do-primes. Once you've gotten a bit of macro-writing experience under your belt, you'll learn to write macros with these kinds of leaks preplugged. It's actually fairly simple if you follow these rules of thumb:

• Unless there's a particular reason to do otherwise, include any subforms in the expansion in positions that will be evaluated in the same order as the subforms appear in the macro call.

• Unless there's a particular reason to do otherwise, make sure subforms are evaluated only once by creating a variable in the expansion to hold the value of evaluating the argument form and then using that variable anywhere else the value is needed in the expansion.

• Use GENSYM at macro expansion time to create variable names used in the expansion.

Macro-Writing Macros

Of course, there's no reason you should be able to take advantage of macros only when writing functions. The job of macros is to abstract away common syntactic patterns, and certain patterns come up again and again in writing macros that can also benefit from being abstracted away.

In fact, you've already seen one such pattern—many macros will, like the last version of do-primes, start with a LET that introduces a few variables holding gensymed symbols to be used in the macro's expansion. Since this is such a common pattern, why not abstract it away with its own macro?

In this section you'll write a macro, with-gensyms, that does just that. In other words, you'll write a macro-writing macro: a macro that generates code that generates code. While complex macro-writing macros can be a bit confusing until you get used to keeping the various levels of code clear in your mind, with-gensymsis fairly straightforward and will serve as a useful but not too strenuous mental limbering exercise.

You want to be able to write something like this:

(defmacro do-primes ((var start end) &body body)

(with-gensyms (ending-value-name)

`(do ((,var (next-prime ,start) (next-prime (1+ ,var)))

(,ending-value-name ,end))

((> ,var ,ending-value-name))

,@body)))

and have it be equivalent to the previous version of do-primes. In other words, the with-gensymsneeds to expand into a LET that binds each named variable, ending-value-namein this case, to a gensymed symbol. That's easy enough to write with a simple backquote template.

(defmacro with-gensyms ((&rest names) &body body)

`(let ,(loop for n in names collect `(,n (gensym)))

,@body))

Note how you can use a comma to interpolate the value of the LOOP expression. The loop generates a list of binding forms where each binding form consists of a list containing one of the names given to with-gensymsand the literal code (gensym). You can test what code the LOOP expression would generate at the REPL by replacing nameswith a list of symbols.

CL-USER> (loop for n in '(a b c) collect `(,n (gensym)))

((A (GENSYM)) (B (GENSYM)) (C (GENSYM)))

After the list of binding forms, the body argument to with-gensymsis spliced in as the body of the LET . Thus, in the code you wrap in a with-gensymsyou can refer to any of the variables named in the list of variables passed to with-gensyms.

If you macro-expand the with-gensymsform in the new definition of do-primes, you should see something like this:

(let ((ending-value-name (gensym)))

`(do ((,var (next-prime ,start) (next-prime (1+ ,var)))

(,ending-value-name ,end))

((> ,var ,ending-value-name))

,@body))

Looks good. While this macro is fairly trivial, it's important to keep clear about when the different macros are expanded: when you compile the DEFMACRO of do-primes, the with-gensymsform is expanded into the code just shown and compiled. Thus, the compiled version of do-primesis just the same as if you had written the outer LET by hand. When you compile a function that uses do-primes, the code generated by with-gensymsruns generating the do-primesexpansion, but with-gensymsitself isn't needed to compile a do-primesform since it has already been expanded, back when do-primeswas compiled.

Another classic macro-writing MACRO: ONCE-ONLY

Another classic macro-writing macro is once-only, which is used to generate code that evaluates certain macro arguments once only and in a particular order. Using once-only, you could write do-primesalmost as simply as the original leaky version, like this:

(defmacro do-primes ((var start end) &body body)

(once-only (start end)

`(do ((,var (next-prime ,start) (next-prime (1+ ,var))))

((> ,var ,end))

,@body)))

However, the implementation of once-onlyis a bit too involved for a blow-by-blow explanation, as it relies on multiple levels of backquoting and unquoting. If you really want to sharpen your macro chops, you can try to figure out how it works. It looks like this:

(defmacro once-only ((&rest names) &body body)

(let ((gensyms (loop for n in names collect (gensym))))

`(let (,@(loop for g in gensyms collect `(,g (gensym))))

`(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))

,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))

,@body)))))

Beyond Simple Macros

I could, of course, say a lot more about macros. All the macros you've seen so far have been fairly simple examples that save you a bit of typing but don't provide radical new ways of expressing things. In upcoming chapters you'll see examples of macros that allow you to express things in ways that would be virtually impossible without macros. You'll start in the very next chapter, in which you'll build a simple but effective unit test framework.

9. Practical: Building a Unit Test Framework

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

Интервал:

Закладка:

Сделать

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

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


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

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

x