Показаны сообщения с ярлыком cl. Показать все сообщения
Показаны сообщения с ярлыком cl. Показать все сообщения

понедельник, 12 сентября 2011 г.

Паттерны для создания рекурсивных функций.

Читая An Introduction to Programming in Emacs Lisp, наткнулся на описание простых рекурсивных паттернов и решил сделать их описание.

Паттерн every.

В данном паттерне действие выполняется над каждым элементом списка. Алгоритм паттерна следующий:


  • Если список пустой, то возвращаем nil.

  • В противном случае работаем над началом списка(операция car). После чего делаем рекурсивный вызов с использованием cdr или комбинируем результат функции с помощью cons.

Пример использования:

(defun square-each (numbers-list)
"Square each of a NUMBERS LIST, recursively."
    (if (not numbers-list)
        nil
        (cons (* (car numbers-list) (car numbers-list)) 
            (square-each (cdr numbers-list)))))

(square-each ’(1 2 3))
⇒ (1 4 9)
В данном примере каждый элемент списка возводится в квадрат.
Еще один пример:

(defun print-elements-recursively (list)
"Print each element of LIST on a line of its own. Uses recursion."
    (when list
        (print (car list))
        (print-elements-recursively (cdr list))))
В приведенном примере функция рекурсивно выводит каждый элемент.

Паттерн accumulate.

В этом паттерне действие производится над каждым элементом списка и результат
этого действия аккамулируется с результатом последующих элементов. Алгоритм имеет вид:


  • Если список пуст, то возвращается нуль или любая другая константа.

  • Иначе берется первый элемент списка и объеденяется с последующими элементами
    рекурсивного вызова с помощью функции + или подобными ей комбинирующими функциями.
Пример:

(defun add-elements (numbers-list)
"Add the elements of NUMBERS-LIST together."
      (if (not numbers-list)
         0
         (+ (car numbers-list) (add-elements (cdr numbers-list)))))

(add-elements ’(1 2 3 4))
⇒ 10
Таким образом функция выводит сумму всех элементов списка.

Паттерн keep.

В данном паттерне каждый элемент списка проходит проверку. Если он удовлетворяет условию, то результат действия сохраняется.
Алгоримт имеет вид:

  • Если список пуст, то возвращаем nil.
  • Если начальный элемент списка проходит проверку, то комбинируем его с помощью cons и делаем рекурсивный вызов с хвостом списка(cdr).
  • В противном случае если первый элемент не удовлетворяет условию, то действие над данным элементом пропускается и с помощью рекурсивного вызова переходим на следующий элемент.
Пример:

(defun keep-three-letter-words (word-list)
"Keep three letter words in WORD-LIST."
     (cond ((not word-list) nil)
           ((eq 3 (length (symbol-name (car word-list))))
               (cons (car word-list) (keep-three-letter-words (cdr word-list))))
           (t (keep-three-letter-words (cdr word-list)))))

(keep-three-letter-words ’(one two three four five six))
⇒ (one two six)
В выше приведенном примере выделяется список символов, длина которого равна 3.

вторник, 6 сентября 2011 г.

Перевел cl-event-callback на bordeaux-threads.

Заменил sb-thread на bordeaux-threads. Алгоритмы работы модуля остались без изменений. Теперь необходимо добавить потоко-безопасность.

среда, 24 августа 2011 г.

Пакет cl-event-callback

       Оформил код в виде пакета. Но пока есть минус - так как использую потоки sbcl, то он работает только под ним, поэтому собираюсь перевести с sbcl-threads на bordeaux-threads.  Ссылка на проект: github.

Пакет обработки событий на lisp

     Мне понадобился модуль, поддерживающий обработку событий, но я ничего не нашел(возможно плохо искал). Поэтому решил написать пакет, решающий эту задачу, исходники которого находятся на github
     Итак, реализация  похожа на сигнал-слотовую модель(например, как в Qt), то есть при определенном событии происходит подписанное на него действие. Пока реализовано:
  1. Три состояние события - событие произошло(:trig),  сброс в исходное состояние (:not-trig) и выполнение подписанного  на событие действия один раз (:trig-stop)  
  2. Добавление/удаление именованного события и его обработчика
  3. Запуск и останов событийной модели
  4. Автоматическое и ручное управление состояниями события

Пример использования:
1. Автоматическое управление


(in-package #:cl-event-callback)

;Объявим переменную, при изменении которой будет присходить событие
(defparameter *a* 1)

; Данная функция будет выполняться при получении события
(defun test () (print "test"))

;Создаем именнованное событие. В данном случае, если *a* больше 3, то 
;будет вызываться функция test.
(defun new-event ()
       (connect '(test) 'test-event '(> *a* 3)))

;Функция для изменения состояния переменной *a*
(defun event-change (var)
       (setq *a* var))

;Удаляем именнованое событие
(defun del-test-event ()
       (disconnect 'test-event))

(defun main ()
       (new-event)		; <- Создаем событие
       (thread-run-callbacks)   ; <- Отслеживаем изменения состояния
       (event-change 5)         ; <- Изменяем переменную *a*, чем вызываем событие
       (sleep 3)
       (event-change 1)         ; <- Возвращаем состояние *a*
       (del-test-event)         ; <- Удаляем событие
       (thread-stop-callbacks))

2. Ручное управление


(in-package #:cl-event-callback)

;Объявим функции для тестов
(defun test1 () (print "test1"))

(defun test2 () (print "test2"))

;Создадим события
(defun create-events()
       (connect '(test1) 'test-one)
       (connect '(test2) 'test-two))

;Функция удаления событий
(defun delete-events ()
       (disconnect 'test-one)
       (disconnect 'test-two))


(defun main ()
       (create-events)			     ; <- Создаем события
       (thread-run-callbacks)                ; <- Отслеживаем изменения сотояния событий
       (set-event-state 'test-one :trig)     ; <- Вызываем событие вручную
       (set-event-state 'test-two :trig-stop); <- Функция, подписанная на это событие вызовется один раз, после чего состояние события переведется в :not-trig
       (sleep 2)
       (set-event-state 'test-one :not-trig) ; <- Это событие переводим в состояние :not-trig
       (delete-events)
       (thread-stop-callbacks))
На текущий момент необходимо доделать следующее:
  • Оформить код в виде пакета
  • Добавить потоко-безопасность

четверг, 30 июня 2011 г.

Простая установка пакетов при помощи asdf.


     Одним из вариантов установки пакетов в sbcl является asdf. Если пакет можно установить из репозитория, то можно воспользоваться пакетом  asdf-install. Для начала нужно подгрузить данный модуль:
                                                                   
(require 'asdf-install)   
или
                                                                                                                          
(require "asdf-install")
После чего выполняем следующую команду:
                                                                       
(asdf-install:install :package-name)
где package-name - это название устанавливаемого пакета.
     Можно так же устанавливать пакет из исходных кодов. В этом  случае делаются следующие шаги:
1. По умолчанию пакеты ставятся в ~/.sbcl, поэтому делаем
символьную ссылку в директории ~/.sbcl/system на package_name.asdf
2. Если скаченный пакет не находится в директории ~/.sbcl/site,
то необходимо добавить путь в asdf командой
                                                                                 
(push (truename #P"/path/to/packet") asdf:*central-registry*) 
3. После чего устанавливаем пакет командой:
                                                                                   
(asdf:oos 'asdf:load-op :package_name)