Новосибирский Государственный Университет
Опубликован: 05.02.2007 | Доступ: свободный | Студентов: 2223 / 413 | Оценка: 4.30 / 4.23 | Длительность: 10:15:00
Лекция 7:

Отображения и функционалы

< Лекция 6 || Лекция 7: 123 || Лекция 8 >
Аннотация: После изучения идеального Лиспа переходим к знакомству с приемами создания его практичных расширений. В данной лекции мы рассмотрим базовые средства обработки чисел и строк, примеры которых помогут разобраться с техникой программирования отображений. При определении отображений вполне естественно используются безымянные функции. Приведены примеры определения фильтров и сверток (редукций).

Отображения обычно используются при анализе и обработке данных, представляющих информацию разной природы. Нумерация, кодирование, идентификация, шифрование - каждый их этих процессов использует исходное множество номеров, текстов, идентификаторов, сообщений, по которым конкретная отображающая функция находит занумерованный объект, строит закодированный текст, выделяет идентифицированный фрагмент, получает зашифрованное сообщение. Таким же образом работает любое введение обозначений - от знака происходит переход к его смыслу. Перевод с одного языка на другой, фотография, киносъемка, спортивный репортаж - все это можно рассматривать как примеры отображений.

При определении отображений прежде всего должны быть ясны следующие вопросы:

  • что представляет собой отображающая функция;
  • как организовано данное, представляющее отображаемое множество;
  • каким способом выделяются элементы отображаемого множества.

При обходе структуры, представляющей множество, отображающая функция будет применена к каждому элементу множества, следовательно может быть выработана подобная структура множества результатов. Возможно, не все полученные результаты нужны, поэтому целесообразно прояснить заранее еще ряд вопросов:

  • где размещается множество всех полученных результатов;
  • чем отличаются нужные результаты от полученных попутно;
  • как строится итоговое данное из отобранных результатов.

Ответ на каждый из таких вопросов может быть дан в виде отдельной функции или функционала. Роль каждой функции в схеме реализации отображения достаточно четко фиксирована. Более точно, функционал может оперировать функциями в качестве аргументов или результатов. Отображающие функции могут быть достаточно общими, универсальными, полезными при определении разных отображений, - они получают имена для многократного использования в разных системах определений. Но могут быть и частными, разовыми, нужными лишь в данном конкретном случае – тогда можно обойтись без имен, использовать тело определения непосредственно в точке вызова функции как значение аргумента.

Таким образом, определение отображения может быть декомпозировано на части (функции и функционалы ) разного назначения, типичного для многих схем информационной обработки. Это позволяет упрощать отладку систем определений, повышать коэффициент повторного использования отлаженных функций. Применение отображений требует дополнительных пояснений, которые и являются целью этой лекции.

Числа и строки

Любую информацию можно представить символьными выражениями. В качестве основных видов символьных выражений выбраны списки и атомы. Атом - неделимое данное, представляющее информацию произвольной природы. Но во многих случаях знание природы информации дает более четкое понимание особенностей изучаемых механизмов.

Программирование работы с числами и строками – привычная, хорошо освоенная область информационной обработки, удобная для оценки преимуществ использования функционалов. Опуская технические подробности, просто отметим, что числа и строки рассматриваются как самоопределимые атомы, смысл которых не требует никакого ассоциирования, он понятен просто по виду записи. Например, натуральные числа записываются без особенностей и могут быть почти произвольной длины:

1
-123
9876543210000000000000123456789

Можно работать с дробными и вещественными числами:

8/12              ;=  2/3
3.1415926

Строки заключаются в обычные двойные кавычки:

"строка любой длины, из произвольных символов, включая все что угодно"

Со строками можно при необходимости работать посимвольно, хотя они рассматриваются как атомы.

(string-equal "строка 1" " строка1");=Nil
(ATOM "a+b-c")      ;= T
(char  "стр1" 4 )   ;= "1"

Список - составное данное, первый элемент которого может рассматриваться как функция, применяемая к остальным элементам, также представленным как символьные выражения.

Это относится и к операциям над числами и строками.

Большинство операций над числами при префиксной записи естественно рассматривать как мультиоперации от произвольного числа аргументов.

(+ 1 2 3 4 5 6)      ;= 21 
(- 12  6   3)        ;= 3
(/  3 5)             ;= 3/5

Любое данное можно превратить в константу, поставив перед ним "'" апостроф. Это эквивалентно записи со специальной функцией " QUOTE ". Для чисел и строк в этом нет необходимости, но это не запрещено:

'1   ;= 1
'"abc" ;=  "abc"

Отказ от барьера между представлениями функций и значений дает возможность символьные выражения использовать как для изображения заданных значений, включая любые структуры над числами и строками, так и для определения функций, обрабатывающих любые данные. (Напоминаем, что определение функции - данное.)

Функционалы - это функции, которые используют в качестве аргументов или результатов другие функции. При построении функционалов переменные могут выполнять роль имен функций, определения которых находятся во внешних формулах, использующих функционалы.

Таблица 7.1. Базовые фукнции над числами
(= Число … ) Истина, если разница между любыми двумя аргументами равна нулю
(/= Число … ) Истина, если никакие два аргумента не равны между собой
(> Число … ) Истина, если каждый аргумент превышает прешественника
(< Число … ) Истина, если каждый аргумент меньше прешественника
(<= Число … ) Истина, если каждый аргумент меньше или равен прешественнику
(>= Число … ) Истина, если каждый аргумент превышает или равен прешественнику
(* Число … ) Произведение произвольного числа аргументов. 1 при их отсутствии.
(+ Число … ) Сумма произвольного числа аргументов. 0 при их отсутствии.
(- Число … ) Эквивалентно расстановке минусов между аргументами, т.е. (- a b c ) = a – b – c
(/ Число … ) Первое число делится на произведение остальных, среди которых не должно быть нуля.
(1+ Число ) ( + Число 1)
(1- Число ) ( - Число 1)
(Boole Операция Целое1 Целое2 ) Вычисляет результат применеия побитовой Операции к двум Целым.
(Gcd Число …) Наибольший общий делитель. Ноль без аргументов.
(Lcm Число … ) Наименьшее общее произведение, 1 при отсутствии аргументов.
(min Число …)
(max Число …)

Функционалы

Рассмотрим технику использования функционалов и наметим, как от простых задач перейти к более сложным.

(defun next (xl) ; Следующие числа:
     (cond       ; пока список не пуст
        (xl (cons (1+ (car xl))   ; прибавляем 1 к его голове
                  (next (cdr xl)) ; и переходим к остальным,
)    )  )   )                     ; собирая результаты в список

(next '(1 2 5 ))    ; = (2 3 6 )
7.1. Для каждого числа из заданного списка получить следующее за ним число и все результаты собрать в список.
(defun 1st (xl)                  ; "головы" элементов = CAR
     (cond                       ; пока список не пуст
        (xl (cons (caar xl)      ; выбираем CAR от его головы
                  (1st (cdr xl)) ; и переходим к остальным,
)    )  )   )                    ; собирая результаты в список

(1st '((один два )(one two )(1 2 )) )  ; = (один one 1)
7.2. Построить список из "голов" элементов списка
(defun lens (xl)                   ; Длины элементов
     (cond                         ; Пока список не пуст
        (xl (cons (length (car xl)); вычисляем длину его головы
                  (lens (cdr xl))  ; и переходим к остальным,
)    )  )   )                      ; собирая результаты в список

(lens '((1 2 ) () (a b c d ) (1 (a b c d ) 3 )) )  ; = (2 0 4 3 )
7.3. Выяснить длины элементов списка

Внешние отличия в записи этих трех функций малосущественны, что позволяет ввести более общую функцию map-el, в определении которой имена " car ", " 1+ " и " length " могут быть заданы как значения параметра fn:

(defun map-el (fn xl)  ; Поэлементное преобразование XL
                        ; с помощью функции FN.
     (cond              ; Пока XL не пуст,
        (xl (cons (funcall fn (car xl) )   
                        ; применяем FN как функцию к голове XL 

             (map-el fn (cdr xl))
			            ; и переходим к продолжению списка,                                                          
)    )  )   )           ; собирая результаты в новый список.

Примечание: funcall – это аналог apply, не требующий заключения аргументов в общий список:

(APPLY (DEFUN Пара (x y) (CONS x y))
             (QUOTE (A  (B  C))));=(A B C)

(FUNCALL (DEFUN Пара (x y) (CONS x y))
                 (QUOTE A) 
                 (QUOTE (B  C)));=(A B C)

Эффект функций next, 1st и lens можно получить выражениями:

(map-el #'1+ xl)  ;следующие числа
(map-el #'car xl) ;"головы" элементов = CAR

#’ x ;= (FUNCTION x) - сокращенное обозначение функции-значения соответственно.

(map-el #'length xl) ; Длины элементов

 (map-el #'1+ '(1 2 5 ))   ; = (2 3 6 )
 (map-el #'car '
         ((один два)(one two)(1 2)));=(один one 1)
 (map-el #'length '((1 2)()(a b c d)(1(a b c d)3))) 
                             ; = (2 0 4 3 )

Эти определения функций формально эквивалентны ранее приведенным – они сохраняют отношение между аргументами и результатами.

Все три примера можно решить с помощью таких определяющих выражений:

(defun next (xl) (map-el #'1+ xl )) 
       ; Очередные числа:
(defun 1st  (xl) (map-el #'car xl ))
       ; "головы" элементов = CAR
(defun lens (xl) (map-el #'length xl )) 
       ; Длины элементов

Параметром функционала может быть любая вспомогательная функция.

(defun sqw (x) (* x x)); Возведение числа в квадрат
(sqw 3)                ; = 9
7.4. Пусть дана вспомогательная функция sqw, возводящая числа в квадрат

Построить список квадратов чисел, используя функцию sqw:

(defun sqware (xl) ; ; Возведение списка чисел в квадрат
     (cond            ; Пока аргумент не пуст,
        (xl (cons (sqw (car xl))
		              ; применяем sqw к его голове
                  (sqware (cdr xl)) 
				      ; и переходим к хвосту списка,
)    )  )   )         ; собирая результаты в список

(sqware '(1 2 5 7 )); = (1 4 25 49 )

Можно использовать map-el:

(defun sqware (xl) (map-el #'sqw xl))

Ниже приведено определение функции sqware- без вспомогательной функции, выполняющее умножение непосредственно. Оно влечет двойное вычисление (CAR xl), т.е. такая техника не вполне эффективна:

(defun sqware- (xl)
     (cond
        (xl (cons (* (car xl) (car xl) ); квадрат головы
        ; вычислять приходится дважды
        (sqware- (cdr xl))
)    )  )   )
< Лекция 6 || Лекция 7: 123 || Лекция 8 >