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», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.
Интервал:
Закладка:
With this macro defined, the define-binary-type
form given previously expands to this code:
(progn
(defmethod read-value ((#:g1618 (eql 'iso-8859-1-string)) in &key length)
(let ((string (make-string length)))
(dotimes (i length)
(setf (char string i) (code-char (read-byte in))))
string))
(defmethod write-value ((#:g1618 (eql 'iso-8859-1-string)) out string &key length)
(dotimes (i length)
(write-byte (char-code (char string i)) out))))
Of course, now that you've got this nice macro for defining binary types, it's tempting to make it do a bit more work. For now you should just make one small enhancement that will turn out to be pretty handy when you start using this library to deal with actual formats such as ID3 tags.
ID3 tags, like many other binary formats, use lots of primitive types that are minor variations on a theme, such as unsigned integers in one-, two-, three-, and four-byte varieties. You could certainly define each of those types with define-binary-type
as it stands. Or you could factor out the common algorithm for reading and writing n -byte unsigned integers into helper functions.
But suppose you had already defined a binary type, unsigned-integer
, that accepts a :bytes
parameter to specify how many bytes to read and write. Using that type, you could specify a slot representing a one-byte unsigned integer with a type specifier of (unsigned-integer :bytes 1)
. But if a particular binary format specifies lots of slots of that type, it'd be nice to be able to easily define a new type—say, u1
—that means the same thing. As it turns out, it's easy to change define-binary-type
to support two forms, a long form consisting of a :reader
and :writer
pair and a short form that defines a new binary type in terms of an existing type. Using a short form define-binary-type
, you can define u1
like this:
(define-binary-type u1 () (unsigned-integer :bytes 1))
which will expand to this:
(progn
(defmethod read-value ((#:g161887 (eql 'u1)) #:g161888 &key)
(read-value 'unsigned-integer #:g161888 :bytes 1))
(defmethod write-value ((#:g161887 (eql 'u1)) #:g161888 #:g161889 &key)
(write-value 'unsigned-integer #:g161888 #:g161889 :bytes 1)))
To support both long- and short-form define-binary-type
calls, you need to differentiate based on the value of the spec
argument. If spec
is two items long, it represents a long-form call, and the two items should be the :reader
and :writer
specifications, which you extract as before. On the other hand, if it's only one item long, the one item should be a type specifier, which needs to be parsed differently. You can use ECASE
to switch on the LENGTH
of spec
and then parse spec
and generate an appropriate expansion for either the long form or the short form.
(defmacro define-binary-type (name (&rest args) &body spec)
(ecase (length spec)
(1
(with-gensyms (type stream value)
(destructuring-bind (derived-from &rest derived-args) (mklist (first spec))
`(progn
(defmethod read-value ((,type (eql ',name)) ,stream &key ,@args)
(read-value ',derived-from ,stream ,@derived-args))
(defmethod write-value ((,type (eql ',name)) ,stream ,value &key ,@args)
(write-value ',derived-from ,stream ,value ,@derived-args))))))
(2
(with-gensyms (type)
`(progn
,(destructuring-bind ((in) &body body) (rest (assoc :reader spec))
`(defmethod read-value ((,type (eql ',name)) ,in &key ,@args)
,@body))
,(destructuring-bind ((out value) &body body) (rest (assoc :writer spec))
`(defmethod write-value ((,type (eql ',name)) ,out ,value &key ,@args)
,@body)))))))
The Current Object Stack
One last bit of functionality you'll need in the next chapter is a way to get at the binary object being read or written while reading and writing. More generally, when reading or writing nested composite objects, it's useful to be able to get at any of the objects currently being read or written. Thanks to dynamic variables and :around
methods, you can add this enhancement with about a dozen lines of code. To start, you should define a dynamic variable that will hold a stack of objects currently being read or written.
(defvar *in-progress-objects* nil)
Then you can define :around
methods on read-object
and write-object
that push the object being read or written onto this variable before invoking CALL-NEXT-METHOD
.
(defmethod read-object :around (object stream)
(declare (ignore stream))
(let ((*in-progress-objects* (cons object *in-progress-objects*)))
(call-next-method)))
(defmethod write-object :around (object stream)
(declare (ignore stream))
(let ((*in-progress-objects* (cons object *in-progress-objects*)))
(call-next-method)))
Note how you rebind *in-progress-objects*
to a list with a new item on the front rather than assigning it a new value. This way, at the end of the LET
, after CALL-NEXT-METHOD
returns, the old value of *in-progress-objects*
will be restored, effectively popping the object of the stack.
With those two methods defined, you can provide two convenience functions for getting at specific objects in the in-progress stack. The function current-binary-object
will return the head of the stack, the object whose read-object
or write-object
method was invoked most recently. The other, parent-of-type
, takes an argument that should be the name of a binary object class and returns the most recently pushed object of that type, using the TYPEP
function that tests whether a given object is an instance of a particular type.
(defun current-binary-object () (first *in-progress-objects*))
(defun parent-of-type (type)
(find-if #'(lambda (x) (typep x type)) *in-progress-objects*))
These two functions can be used in any code that will be called within the dynamic extent of a read-object
or write-object
call. You'll see one example of how current-binary-object
can be used in the next chapter. [270] The ID3 format doesn't require the parent-of-type function since it's a relatively flat structure. This function comes into its own when you need to parse a format made up of many deeply nested structures whose parsing depends on information stored in higher-level structures. For example, in the Java class file format, the top-level class file structure contains a constant pool that maps numeric values used in other substructures within the class file to constant values that are needed while parsing those substructures. If you were writing a class file parser, you could use parent-of-type in the code that reads and writes those substructures to get at the top-level class file object and from there to the constant pool.
Интервал:
Закладка:
Похожие книги на «Practical Common Lisp»
Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.
Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.