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», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.
Интервал:
Закладка:
Now you can implement methods on this generic function that specialize on type
with EQL
specializers and return column
objects with the slots filled in with appropriate values. Here's the generic function and methods that define column types for the type names string
and number
:
(defmethod make-column (name (type (eql 'string)) &optional default-value)
(make-instance
'column
:name name
:comparator #'string<
:equality-predicate #'string=
:default-value default-value
:value-normalizer #'not-nullable))
(defmethod make-column (name (type (eql 'number)) &optional default-value)
(make-instance
'column
:name name
:comparator #'<
:equality-predicate #'=
:default-value default-value))
The following function, not-nullable
, used as the value-normalizer
for string
columns, simply returns the value it's given unless the value is NIL
, in which case it signals an error:
(defun not-nullable (value column)
(or value (error "Column ~a can't be null" (name column))))
This is important because STRING<
and STRING=
will signal an error if called on NIL
; it's better to catch bad values before they go into the table rather than when you try to use them. [293] As always, the first causality of concise exposition in programming books is proper error handling; in production code you'd probably want to define your own error type, such as the following, and signal it instead: (error 'illegal-column-value :value value :column column) Then you'd want to think about where you can add restarts that might be able to recover from this condition. And, finally, in any given application you could establish condition handlers that would choose from among those restarts.
Another column type you'll need for the MP3 database is an interned-string
whose values are interned as discussed previously. Since you need a hash table in which to intern values, you should define a subclass of column
, interned-values-column
, that adds a slot whose value is the hash table you use to intern.
To implement the actual interning, you'll also need to provide an :initform
for value-normalizer
of a function that interns the value in the column's interned-values
hash table. And because one of the main reasons to intern values is to allow you to use EQL
as the equality predicate, you should also add an :initform
for the equality-predicate
of #'eql
.
(defclass interned-values-column (column)
((interned-values
:reader interned-values
:initform (make-hash-table :test #'equal))
(equality-predicate :initform #'eql)
(value-normalizer :initform #'intern-for-column)))
(defun intern-for-column (value column)
(let ((hash (interned-values column)))
(or (gethash (not-nullable value column) hash)
(setf (gethash value hash) value))))
You can then define a make-column
method specialized on the name interned-string
that returns an instance of interned-values-column
.
(defmethod make-column (name (type (eql 'interned-string)) &optional default-value)
(make-instance
'interned-values-column
:name name
:comparator #'string<
:default-value default-value))
With these methods defined on make-column
, you can now define a function, make-schema
, that builds a list of column
objects from a list of column specifications consisting of a column name, a column type name, and, optionally, a default value.
(defun make-schema (spec)
(mapcar #'(lambda (column-spec) (apply #'make-column column-spec)) spec))
For instance, you can define the schema for the table you'll use to store data extracted from MP3s like this:
(defparameter *mp3-schema*
(make-schema
'((:file string)
(:genre interned-string "Unknown")
(:artist interned-string "Unknown")
(:album interned-string "Unknown")
(:song string)
(:track number 0)
(:year number 0)
(:id3-size number))))
To make an actual table for holding information about MP3s, you pass *mp3-schema*
as the :schema
initarg to MAKE-INSTANCE
.
(defparameter *mp3s* (make-instance 'table :schema *mp3-schema*))
Inserting Values
Now you're ready to define your first table operation, insert-row
, which takes a plist of names and values and a table and adds a row to the table containing the given values. The bulk of the work is done in a helper function, normalize-row
, that builds a plist with a defaulted, normalized value for each column, using the values from names-and-values
if available and the default-value
for the column if not.
(defun insert-row (names-and-values table)
(vector-push-extend (normalize-row names-and-values (schema table)) (rows table)))
(defun normalize-row (names-and-values schema)
(loop
for column in schema
for name = (name column)
for value = (or (getf names-and-values name) (default-value column))
collect name
collect (normalize-for-column value column)))
It's worth defining a separate helper function, normalize-for-column
, that takes a value and a column
object and returns the normalized value because you'll need to perform the same normalization on query arguments.
(defun normalize-for-column (value column)
(funcall (value-normalizer column) value column))
Now you're ready to combine this database code with code from previous chapters to build a database of data extracted from MP3 files. You can define a function, file->row
, that uses read-id3
from the ID3v2 library to extract an ID3 tag from a file and turns it into a plist that you can pass to insert-row
.
(defun file->row (file)
(let ((id3 (read-id3 file)))
(list
:file (namestring (truename file))
:genre (translated-genre id3)
:artist (artist id3)
:album (album id3)
:song (song id3)
:track (parse-track (track id3))
:year (parse-year (year id3))
:id3-size (size id3))))
You don't have to worry about normalizing the values since insert-row
takes care of that for you. You do, however, have to convert the string values returned by the track
and year
into numbers. The track number in an ID3 tag is sometimes stored as the ASCII representation of the track number and sometimes as a number followed by a slash followed by the total number of tracks on the album. Since you care only about the actual track number, you should use the :end
argument to PARSE-INTEGER
to specify that it should parse only up to the slash, if any. [294] If any MP3 files have malformed data in the track and year frames, PARSE-INTEGER could signal an error. One way to deal with that is to pass PARSE-INTEGER the :junk-allowed argument of T , which will cause it to ignore any non-numeric junk following the number and to return NIL if no number can be found in the string. Or, if you want practice at using the condition system, you could define an error and signal it from these functions when the data is malformed and also establish a few restarts to allow these functions to recover.
Интервал:
Закладка:
Похожие книги на «Practical Common Lisp»
Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.
Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.