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», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.
Интервал:
Закладка:
(do-rows (song (songs-for-album playlist (column-value album-row :album)))
(insert-row song new-table)))
(setf (songs-table playlist) new-table)))
(defun shuffled-album-names (playlist)
(shuffle-table
(select
:columns :album
:from (songs-table playlist)
:distinct t)))
(defun songs-for-album (playlist album)
(select
:from (songs-table playlist)
:where (matching (songs-table playlist) :album album)
:order-by :track))
The last manipulation you need to support is setting the playlist's repeat mode. Most of the time you don't need to take any extra action when setting repeat
—its value comes into play only in maybe-move-to-next-song
. However, you need to update the current-song
as a result of changing repeat
in one situation, namely, if current-idx
is at the end of a nonempty playlist and repeat
is being changed to :song
or :all
. In that case, you want to continue playing, either repeating the last song or starting at the beginning of the playlist. So, you should define an :after
method on the generic function (setf repeat)
.
(defmethod (setf repeat) :after (value (playlist playlist))
(if (and (at-end-p playlist) (not (empty-p playlist)))
(ecase value
(:song (setf (current-idx playlist) (1- (table-size (songs-table playlist)))))
(:none)
(:all (setf (current-idx playlist) 0)))
(update-current-if-necessary playlist)))
Now you have all the underlying bits you need. All that remains is the code that will provide a Web-based user interface for browsing the MP3 database and manipulating playlists. The interface will consist of three main functions defined with define-url-function
: one for browsing the song database, one for viewing and manipulating a single playlist, and one for listing all the available playlists.
But before you get to writing these three functions, you need to start with some helper functions and HTML macros that they'll use.
Query Parameter Types
Since you'll be using define-url-function
, you need to define a few methods on the string->type
generic function from Chapter 28 that define-url-function
uses to convert string query parameters into Lisp objects. In this application, you'll need methods to convert strings to integers, keyword symbols, and a list of values.
The first two are quite simple.
(defmethod string->type ((type (eql 'integer)) value)
(parse-integer (or value "") :junk-allowed t))
(defmethod string->type ((type (eql 'keyword)) value)
(and (plusp (length value)) (intern (string-upcase value) :keyword)))
The last string->type
method is slightly more complex. For reasons I'll get to in a moment, you'll need to generate pages that display a form that contains a hidden field whose value is a list of strings. Since you're responsible for generating the value in the hidden field and for parsing it when it comes back, you can use whatever encoding is convenient. You could use the functions WRITE-TO-STRING
and READ-FROM-STRING
, which use the Lisp printer and reader to write and read data to and from strings, except the printed representation of strings can contain quotation marks and other characters that may cause problems when embedded in the value attribute of an INPUT
element. So, you'll need to escape those characters somehow. Rather than trying to come up with your own escaping scheme, you can just use base 64, an encoding commonly used to protect binary data sent through e-mail. AllegroServe comes with two functions, base64-encode
and base64-decode
, that do the encoding and decoding for you, so all you have to do is write a pair of functions: one that encodes a Lisp object by converting it to a readable string with WRITE-TO-STRING
and then base 64 encoding it and, conversely, another to decode such a string by base 64 decoding it and passing the result to READ-FROM-STRING
. You'll want to wrap the calls to WRITE-TO-STRING
and READ-FROM-STRING
in WITH-STANDARD-IO-SYNTAX
to make sure all the variables that affect the printer and reader are set to their standard values. However, because you're going to be reading data that's coming in from the network, you'll definitely want to turn off one feature of the reader—the ability to evaluate arbitrary Lisp code while reading! [309] The reader supports a bit of syntax, #. , that causes the following s-expression to be evaluated at read time. This is occasionally useful in source code but obviously opens a big security hole when you read untrusted data. However, you can turn off this syntax by setting *READ-EVAL* to NIL , which will cause the reader to signal an error if it encounters #. .
You can define your own macro with-safe-io-syntax
, which wraps its body forms in WITH-STANDARD-IO-SYNTAX
wrapped around a LET
that binds *READ-EVAL*
to NIL
.
(defmacro with-safe-io-syntax (&body body)
`(with-standard-io-syntax
(let ((*read-eval* nil))
,@body)))
Then the encoding and decoding functions are trivial.
(defun obj->base64 (obj)
(base64-encode (with-safe-io-syntax (write-to-string obj))))
(defun base64->obj (string)
(ignore-errors
(with-safe-io-syntax (read-from-string (base64-decode string)))))
Finally, you can use these functions to define a method on string->type
that defines the conversion for the query parameter type base64-list
.
(defmethod string->type ((type (eql 'base-64-list)) value)
(let ((obj (base64->obj value)))
(if (listp obj) obj nil)))
Boilerplate HTML
Next you need to define some HTML macros and helper functions to make it easy to give the different pages in the application a consistent look and feel. You can start with an HTML macro that defines the basic structure of a page in the application.
(define-html-macro :mp3-browser-page ((&key title (header title)) &body body)
`(:html
(:head
(:title ,title)
(:link :rel "stylesheet" :type "text/css" :href "mp3-browser.css"))
(:body
(standard-header)
(when ,header (html (:h1 :class "title" ,header)))
,@body
(standard-footer))))
You should define standard-header
and standard-footer
as separate functions for two reasons. First, during development you can redefine those functions and see the effect immediately without having to recompile functions that use the :mp3-browser-page
macro. Second, it turns out that one of the pages you'll write later won't be defined with :mp3-browser-page
but will still need the standard header and footers. They look like this:
Интервал:
Закладка:
Похожие книги на «Practical Common Lisp»
Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.
Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.