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))
Комментариев нет:
Отправить комментарий