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», без необходимости каждый раз заново искать на чём Вы остановились. Поставьте закладку, и сможете в любой момент перейти на страницу, на которой закончили чтение.
Интервал:
Закладка:
But if the column comparator returns NIL
, then you need to determine whether that's because the second value should sort ahead of the first value or because they're equivalent. So you should call the comparator again with the arguments reversed. If the comparator returns true this time, it means the second column value sorts ahead of the first and thus the second row ahead of the first row, so you can return NIL
immediately. Otherwise, the column values are equivalent, and you need to move onto the next column. If you get through all the columns without one row's value ever winning the comparison, then the rows are equivalent, and you return NIL
. A function that implements this algorithm looks like this:
(defun row-comparator (column-names schema)
(let ((comparators (mapcar #'comparator (extract-schema column-names schema))))
#'(lambda (a b)
(loop
for name in column-names
for comparator in comparators
for a-value = (getf a name)
for b-value = (getf b name)
when (funcall comparator a-value b-value) return t
when (funcall comparator b-value a-value) return nil
finally (return nil)))))
Matching Functions
The :where
argument to select
can be any function that takes a row object and returns true if it should be included in the results. In practice, however, you'll rarely need the full power of arbitrary code to express query criteria. So you should provide two functions, matching
and in
, that will build query functions that allow you to express the common kinds of queries and that take care of using the proper equality predicates and value normalizers for each column.
The workhouse query-function constructor will be matching
, which returns a function that will match rows with specific column values. You saw how it was used in the earlier examples of select
. For instance, this call to matching
:
(matching *mp3s* :artist "Green Day")
returns a function that matches rows whose :artist
value is "Green Day". You can also pass multiple names and values; the returned function matches when all the columns match. For example, the following returns a closure that matches rows where the artist is "Green Day" and the album is "American Idiot":
(matching *mp3s* :artist "Green Day" :album "American Idiot")
You have to pass matching
the table object because it needs access to the table's schema in order to get at the equality predicates and value normalizer functions for the columns it matches against.
You build up the function returned by matching
out of smaller functions, each responsible for matching one column's value. To build these functions, you should define a function, column-matcher
, that takes a column
object and an unnormalized value you want to match and returns a function that accepts a single row and returns true when the value of the given column in the row matches the normalized version of the given value.
(defun column-matcher (column value)
(let ((name (name column))
(predicate (equality-predicate column))
(normalized (normalize-for-column value column)))
#'(lambda (row) (funcall predicate (getf row name) normalized))))
You then build a list of column-matching functions for the names and values you care about with the following function, column-matchers
:
(defun column-matchers (schema names-and-values)
(loop for (name value) on names-and-values by #'cddr
when value collect
(column-matcher (find-column name schema) value)))
Now you can implement matching
. Again, note that you do as much work as possible outside the closure in order to do it only once rather than once per row in the table.
(defun matching (table &rest names-and-values)
"Build a where function that matches rows with the given column values."
(let ((matchers (column-matchers (schema table) names-and-values)))
#'(lambda (row)
(every #'(lambda (matcher) (funcall matcher row)) matchers))))
This function is a bit of a twisty maze of closures, but it's worth contemplating for a moment to get a flavor of the possibilities of programming with functions as first-class objects.
The job of matching
is to return a function that will be invoked on each row in a table to determine whether it should be included in the new table. So, matching
returns a closure with one parameter, row
.
Now recall that the function EVERY
takes a predicate function as its first argument and returns true if, and only if, that function returns true each time it's applied to an element of the list passed as EVERY
's second argument. However, in this case, the list you pass to EVERY
is itself a list of functions, the column matchers. What you want to know is that every column matcher, when invoked on the row you're currently testing, returns true. So, as the predicate argument to EVERY
, you pass yet another closure that FUNCALL
s the column matcher, passing it the row.
Another matching function that you'll occasionally find useful is in
, which returns a function that matches rows where a particular column is in a given set of values. You'll define in
to take two arguments: a column name and a table that contains the values you want to match. For instance, suppose you wanted to find all the songs in the MP3 database that have names the same as a song performed by the Dixie Chicks. You can write that where clause using in
and a sub select
like this: [295] This query will also return all the songs performed by the Dixie Chicks. If you want to limit it to songs by artists other than the Dixie Chicks, you need a more complex :where function. Since the :where argument can be any function, it's certainly possible; you could remove the Dixie Chicks' own songs with this query: (let* ((dixie-chicks (matching *mp3s* :artist "Dixie Chicks")) (same-song (in :song (select :columns :song :from *mp3s* :where dixie-chicks))) (query #'(lambda (row) (and (not (funcall dixie-chicks row)) (funcall same-song row))))) (select :columns '(:artist :song) :from *mp3s* :where query)) This obviously isn't quite as convenient. If you were going to write an application that needed to do lots of complex queries, you might want to consider coming up with a more expressive query language.
(select
:columns '(:artist :song)
:from *mp3s*
:where (in :song
(select
:columns :song
:from *mp3s*
:where (matching *mp3s* :artist "Dixie Chicks"))))
Although the queries are more complex, the definition of in
is much simpler than that of matching
.
(defun in (column-name table)
(let ((test (equality-predicate (find-column column-name (schema table))))
(values (map 'list #'(lambda (r) (getf r column-name)) (rows table))))
#'(lambda (row)
(member (getf row column-name) values :test test))))
Интервал:
Закладка:
Похожие книги на «Practical Common Lisp»
Представляем Вашему вниманию похожие книги на «Practical Common Lisp» списком для выбора. Мы отобрали схожую по названию и смыслу литературу в надежде предоставить читателям больше вариантов отыскать новые, интересные, ещё непрочитанные произведения.
Обсуждение, отзывы о книге «Practical Common Lisp» и просто собственные мнения читателей. Оставьте ваши комментарии, напишите, что Вы думаете о произведении, его смысле или главных героях. Укажите что конкретно понравилось, а что нет, и почему Вы так считаете.