1. Пересчёт секунд, минут и часов
Закрепляем уже пройденный материал, пытаясь применить уже известное в чуть-чуть более практической, приземлённой задаче.
;; Переводит часы, минуты и секунды в секунды
(defun hms-to-seconds (h m s)
(+ (* h 3600) (* m 60) s))
;; Переводит секунды в часы, минуты и секунды
(defun seconds-to-hms (sec)
(list (floor (/ sec 3600))
(floor (/ (mod sec 3600) 60))
(mod sec 60)))
;; Проверка правильности показаний часов:
;; часы не показывают отрицательные числа
;; а минуты и секунды не могут быть больше 59
(defun hms-ok (h m s)
(and (>= h 0)
(>= m 0)
(>= s 0)
(< m 60)
(< s 60)))
;; Нормализовать часы, минуты и секунды:
;; перевести их в вид, пригодный для
;; отображения на часах
(defun normalize-hms (h m s)
(seconds-to-hms (hms-to-seconds h m s)))
;; Перевести секунды в минуты,
;; возвращается количество полных минут
(defun seconds-to-minutes (sec)
(floor (/ sec 60)))
;; Перевести секунды в часы,
;; возвращается количество полных часов
(defun seconds-to-hours (sec)
(floor (/ sec 3600)))
;; Получить показания секундной стрелки часов
(defun s-from-seconds (sec)
(mod sec 60))
;; Получить показания минутной стрелки часов
(defun m-from-seconds (sec)
(mod (floor (/ sec 60)) 60))
;; другой вариант одноимённой функции
;(defun m-from-seconds (sec)
; (floor (/ (mod sec 3600) 60)))
;; Получить показания часовой стрелки часов
(defun h-from-seconds (sec)
(floor (/ sec 3600)))
;; другой вариант одноимённой функции
;(defun seconds-to-hms (sec)
; (list (h-from-seconds sec)
; (m-from-seconds sec)
; (s-from-seconds sec)))
2. Пример использования замыканийТрудно объяснить всю нижеследующую магию простыми словами. Здесь используются анонимные функции (они же lambda), передача ссылки на функцию - это ещё можно понять. Но вот переменные в Common Lisp довольно необычны.
Я толком не усвоил терминологию, но своими словами могу сказать следующее: существуют глобальные переменные и локальные переменные. Глобальные переменные доступны из любого места программы, локальные - только в пределах области видимости.
Интересная особенность заключается в том, что можно в любом месте программы создать дополнительную область видимости, внутри которой переопределить значение переменной. Это делается с помощью оператора let. Все обращения к этой переменной внутри блока будут происходить к локальному же значению этой переменной. Вне оператора let переменная восстанавливает своё прежнее значение. Но при этом, в нижеследующем примере, анонимная функция сохраняет внутри себя переопределённую с помощью оператора let переменную и её значение. Это и называется замыканием. Очень необычно.
;;;; Пример использования замыканий
;; Функция, возвращающая фукцию-счётчик
;; Начальное значение счётчика - 0
(defun get-counter ()
(let ((n 0))
(function (lambda ()
(setf n (+ n 1))))))
;; Более лаконичный аналог предыдущей функции
;(defun get-counter ()
; (let ((n 0))
; #'(lambda ()
; (incf n))))
;; Переменная с первой функцией-счётчиком
(setf counter1 (get-counter))
;; Переменная со второй функцией-счётчиком
(setf counter2 (get-counter))
;; Более лаконичный вариант двух предыдущих присваиваний
;(setf counter1 (get-counter)
; counter2 (get-counter))
;;; Попеременно вызываем счётчики
;;; Счётчики при каждом вызове увеличиваются на 1
(funcall counter1)
(funcall counter2)
(funcall counter1)
(funcall counter1)
(funcall counter1)
(funcall counter2)
В этом примере функция counter1 будет наращивать значение своего счётчика, а функция counter2 - значение своего.3. Чуть более сложный пример использования замыканий
Здесь уже используются ассоциативные массивы, а функция get-counter-methods возвращает в ассоциативном массиве три функции, замкнутые на одной переменной.
;;;; Ещё один пример использования замыканий
;; Функция возвращает ассоциативный список
;; из трёх функций, замкнутых на одну переменную
(defun get-counter-methods ()
(let ((n 0))
(list
:increase #'(lambda () (incf n))
:decrease #'(lambda () (decf n))
:value #'(lambda () n))))
;; Создаём счётчик, содержащий ассоциативный
;; список трёх функций
(setf counter (get-counter-methods))
;;; Далее вызываем функции и следим
;;; за состоянием счётчика
(funcall (getf counter :increase))
(funcall (getf counter :value))
(funcall (getf counter :decrease))
(funcall (getf counter :value))

Комментариев нет:
Отправить комментарий