Автор Тема: (cons 'Scheme 'LISP)  (Прочитано 2292 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн korvin

  • Глобальный модератор
  • *****
  • Сообщений: 21
  • Карма: 10
  • Пол: Мужской
  • Archlinux ^_^
    • Просмотр профиля
(cons 'Scheme 'LISP)
« : 18 Январь 2009, 11:14:55 »
И так, тема посвящена замечательным языкам программирования Scheme и LISP. Обсуждаем, спрашиваем, отвечаем, делимся опытом, ссылками, и т.д. и т.п. =)

Вчера решил начать взяться за изучение макросов, оказалось, что они довольно просты, по крайней мере, не так страшны и непонятны, как казалось =) вот простейший макрос на Схеме:
(define-syntax my-if
;;; ^ объявляем имя синтаксической конструкции
  (syntax-rules (then else) 
  ;;; ^ описываем правила: в первом списке -- (then else) --
  ;;;     перечисляем слова, которые будут невычисляемыми,
  ;;;     а ля "зарезервированные слова"
  ;;; в последующих списках описываются шаблоны, 
  ;;;     которые будет обрабатывать макросом и собственно обработчики:
    ((_ condition then on-true else on-false) 
    ;;; ^ здесь описываем шаблон конструкции:
    ;;;     _ -- сюда подставляется имя макроса,
    ;;;     можно написать my-if вместо _ , без разницы
    ;;;     then и else как мы описали раньше просто слово,
    ;;;     condition, on-true и on-false
    ;;;         -- так сказать локальные переменные макроса,
    ;;;     выражения, переданные в них -- вычисляемые
     (if condition on-true on-false)) 
     ;;; а этот ^ список обработчик шаблона.
    ;;; шаблонов может быть несколько, например можно добавить еще 2:
    ((_ condition then on-true)
     (if condition on-true))
    ((_ condition else on-false)
     (if (not condition) on-false))
)) ;;; <- здесь закрываем списки особых форм syntax-rules и define-syntax.
;;; теперь мы можем пользоваться придуманным макросом:
(my-if (> 2 4) then (display "Y\n") else (display "N\n"))
(my-if (> 4 2) then (display "Y\n"))
(my-if (> 4 2) else (display "Y\n"))
« Последнее редактирование: 18 Январь 2009, 13:10:38 от korvin »
настройка фаервола в linux: sudo iptables -I OUTPUT -p tcp -j DROP
(©  '(define  LISP  (такой  язык-программирования  (состоящий-из  смайликов  (чуть  более)  (чем  целиком))))  lurkmore)

Оффлайн korvin

  • Глобальный модератор
  • *****
  • Сообщений: 21
  • Карма: 10
  • Пол: Мужской
  • Archlinux ^_^
    • Просмотр профиля
Re: (cons 'Scheme 'LISP)
« Ответ #1 : 12 Февраль 2009, 17:18:09 »
продолжаю играться с макросами в Схеме, пример реализации операторов инкремента, декремента, +=, -=, *=, /=:
(define-syntax make
  (syntax-rules ()
    ((_ op func)
   (define-syntax op
       (syntax-rules ()
   ((_ x y)
   (begin (set! x (func x y)) x)))))))

(make += +)
(make -= -)
(make *= *)
(make /= /)

(define-syntax ++
  (syntax-rules ()
    ((_ i)
     (begin (set! i (+ i 1)) i))))

(define-syntax --
  (syntax-rules ()
  ((_ i)
     (begin (set! i (- i 1)) i))))

;--------------------------------


(define x1 2)
(define x2 3)
(message "x1 = " x1 "  ; x2 = " x2)
(++ x1)
(+= x2 (-- x2))
(message "x1 = " x1 "  ; x2 = " x2)
(+= x1 x2) (message "x1 = " x1)
(*= x1 x2) (message "x1 = " x1)
(-= x1 x2) (message "x1 = " x1)
(/= x1 x2) (message "x1 = " x1)
Здесь же есть и немного метапрограммирования: как видно из кода, операторы _= реализованы через один макроос, который сам пишет макросы для каждого из операторов, принимая в качестве параметров символ для обозначения оператора и функцию, которую он должен реализовать.

правда ++ и -- работают только как постфиксные ++/-- из Си, как сделать префиксные хз =)
прога на Си для проверки:
#include <stdio.h>

int main( void )
{
   int x1 = 2;
   int x2 = 3;
   printf( "x1 = %d  ; x2 = %d\n" , x1 , x2 );
  x1++;
 x2 += x2--;
   printf( "x1 = %d  ; x2 = %d\n" , x1 , x2 );
   x1 += x2;
    printf( "x1 = %d\n" , x1 );
    x1 *= x2;
    printf( "x1 = %d\n" , x1 );
    x1 -= x2;
    printf( "x1 = %d\n" , x1 );
    x1 /= x2;
    printf( "x1 = %d\n" , x1 );
}

линки на пасту с подсветочкой синтаксиса:
http://openpaste.org/11153/
http://openpaste.org/11154/
« Последнее редактирование: 26 Февраль 2009, 16:37:53 от Green »
настройка фаервола в linux: sudo iptables -I OUTPUT -p tcp -j DROP
(©  '(define  LISP  (такой  язык-программирования  (состоящий-из  смайликов  (чуть  более)  (чем  целиком))))  lurkmore)

Оффлайн korvin

  • Глобальный модератор
  • *****
  • Сообщений: 21
  • Карма: 10
  • Пол: Мужской
  • Archlinux ^_^
    • Просмотр профиля
Re: (cons 'Scheme 'LISP)
« Ответ #2 : 26 Февраль 2009, 00:31:38 »
теперь примерчик с каррингом(приведение функции n аргументов к функции одного аргумента, возвращающей функцию n-1 аргументов) и функций высшего порядка(функции, оперирующие другими функциями):
(define (make-val-list v0 dv vN)
  (define (make-list v)
  (if (= v vN)
  (cons v '())
  (cons v (make-list (+ v dv)))))
  (make-list v0))

(define A-list (make-val-list 1 1 3))
(define x-list (make-val-list 0.2 0.1 1.2))

(define (y A)
  (lambda (x) (- (* A x) (tan (* pi (/ x 4))))))

(define y-list (map (lambda (A)
           (map (y A) x-list))
           A-list))

(display (map (lambda (ls) (apply max ls)) y-list))
пример основан на примерно месяц-два назад промелькнувшей в этом подразделе темы про задачу на С++.
в данном коде функция от(x,A) сведена к функции y(A), возвращающей функцию от(х), т.е. ее можно было бы применять так:
(let ((A1 1) (x1 0.2))
  ((y A1) x1))
карринг очень похож на замыкание как я понял, только замыкание проявляется в следующих действиях:
(define y1 (y 1))  ; создаст функцию (y1 x) = (- (* 1 x) (tan (* pi (/ x 4))))
(define y2 (y 2))  ; создаст функцию (y1 x) = (- (* 2 x) (tan (* pi (/ x 4))))
(y1 x1)
(y2 x1)

в примере применялись функции высшего порядка map и apply, они работают следующим образом:

1) map применяет переданую ей в качестве переданного аргумента функцию к каждому элементу списка, переданного вторым аргументом, и возвращает список значений-результаттов применения, например
(define test-list '(-2 1 7 0 -5 4))
(map abs test-list)
вернет список абсолютных величин значений списка test-list: (2 1 7 0 5 4)

2) apply применяет переданную ей в качестве первого аргумента функцию ко всем элементам списка, переданного вторым аргументом, как буд-то они(элементы списка) являются параметрами переданной функции, например функции
(max -2 1 7 0 -5 4)
(+ -2 1 7 0 -5 4)
вернут максимальное значение и сумму значений переданных в качестве параметров соответственно. однако попытка вызвать их таким образом:
(max test-list)
(+ test-list)
выдаст ошибку. это не очень удобно, когда, например, в ходе работы программы генерируется некий список значений, из которого потом нужно выбрать максимальное значение, или сумму подсчитать. ту-то и приходит на помощь функция apply, которая как-бы раскрывает список, т. е. выражения
(apply max test-list)
(apply + test-list)
эквивалентны выражениям
(max -2 1 7 0 -5 4)
(+ -2 1 7 0 -5 4)
соответственно.
и в завершении вернемся к каррингу. как видно из описания функции map она может оперировать только функциями одной переменной. вот тут-то к нам и приходит на помощь карринг =)
уже почти по традиции ссылка на правильно форматированный код:
http://openpaste.org/11622/
« Последнее редактирование: 26 Февраль 2009, 16:39:15 от Green »
настройка фаервола в linux: sudo iptables -I OUTPUT -p tcp -j DROP
(©  '(define  LISP  (такой  язык-программирования  (состоящий-из  смайликов  (чуть  более)  (чем  целиком))))  lurkmore)

Оффлайн korvin

  • Глобальный модератор
  • *****
  • Сообщений: 21
  • Карма: 10
  • Пол: Мужской
  • Archlinux ^_^
    • Просмотр профиля
Re: (cons 'Scheme 'LISP)
« Ответ #3 : 26 Февраль 2009, 09:04:20 »
немного о функция и макросах с произвольным количеством аргументов. как вы уже заметили, некоторые функции и формы могут принимать произвольное количество аргументов, например:
(+ 1 2 3)
(+ 1 2 3 4)
(and exp1 exp2)
(and exp1 exp2 exp3 exp4)
что бы создать подобную функцию, необходимо использовать точечную запись аргументов, например:
(define (message head . tail)
  (display head)
  (for-each display tail)
  (newline))
в таком определении функции первый аргумент будет передаваться в нее непосредственно, параметром head, а все остальные -- списком tail. здесь используется еще одна функция высшего порядка for-each, которая применяет функцию, переданную в качестве первого аргумента к каждому элементу списка, переданного вторым аргументом, но в отличие от функции map не возвращает никакого значения. теперь можно так применять функцию message:
(message "Hello World!")
(let ((world "World"))
  (message "Hello " world "!"))

определить форму, принимающую произвольное количество аргумента, можно подобным образом:
(define-syntax and
  (syntax-rules ()
   ((_) #t)
  ((_ e) e)
 ((_ e1 e2 e3 ...)
  (if e1 (and e2 e3 ...) #f))))
тогда можно будет использовать новую форму таким образом:
(let ((x 2) (x-min 1) (x-max 3))
  (and (> x x-min) (< x x-max) (not (= x 0))))


http://openpaste.org/11642/
« Последнее редактирование: 26 Февраль 2009, 16:54:16 от korvin »
настройка фаервола в linux: sudo iptables -I OUTPUT -p tcp -j DROP
(©  '(define  LISP  (такой  язык-программирования  (состоящий-из  смайликов  (чуть  более)  (чем  целиком))))  lurkmore)