Привет. В данной статье хотелось бы еще разок осветить вопрос объектного программирования на языке Scheme, так, как его рассматривают в книге «Структура и интерпретация компьютерных программ». Далее предлагаю тем, кто еще ни когда не программировал на Scheme скачать DrRacket и попробовать по шагам пройтись по примерам из данной статьи. Опытные программисты Scheme, Racket… эта статья будет очень скучна, так как написана для новичков и людей, желающих «потрогать» racket.
И так по порядку. Разомнемся для начала в DrRacket IDE.
Создадим новый файл программы. Сохраним его.
Вставим в начале файла директиву указывающую на язык:
#lang racket
Определим функцию:
(define (func1 x) x) ; принимает на вход x, возвращает x
Использовать функцию можно так:
(func1 10) ; результат будет 10
Теперь определим другую функцию:
(define (func2) "func2")
А теперь попробуем определить переменную такую, что она применяет первую функцию ко второй и возвращает вторую функцию:
(define x (func1 func2)) ; теперь x - это объект экземпляра функции func2
Использовать x нужно как функцию:
(x) ; вернет стоку "func2"
Тут мы использовали возможность того, что функция может возвращать функции, а их результат можно определить как переменные.
Далее давайте создадим объект, инкапсулирующий в себе внутренние переменные и другие функции:
(define (MyObject field1 field2) ; объект у которого при конструировании будут две переменные field1 и field2 (let ((f1 field1) ; инициализация внутренней переменной f1, с помощью field1 (f2 field2)) ; ... (define (get-f1) f1) ; вернуть значение внутреннего поля f1 (define (get-f2) f2) ; ... (define (set-f1 x) (set! f1 x)) ; присваиваем f1 значение x (define (set-f2 x) (set! f2 x)) ; ... ; далее идет самое интересное (define (dispatch m) ; функция диспетчирования функций в нашем объекте (cond ((eq? m 'get-f1) get-f1) ; если m равно get-f1, то возвращается функция get-f1 ((eq? m 'set-f1) set-f1) ; ... ((eq? m 'get-f2) get-f2) ; ... ((eq? m 'set-f2) set-f2) ; ... ) ) dispatch)) ; тут мы в функции MyObject возвращаем функцию диспетчирования
Ну вот объект определен, теперь сделаем экземпляр объекта MyObject:
(define Obj1 (MyObject " Hello " " world!!! ")) ; теперь Obj1 экземпляр
Далее просто используем экземпляр так как захотим:
(display ((Obj1 'get-f1))) ; тут ф-ия display что-то типа printf, а двойные скобки ; в ((Obj1 'get-f1)) надо писать для того, чтобы вычислялась функция get-f1 (display ((Obj1 'get-f2))) ; аналогично (newline) ; переход на новую строку ; результатом будет " Hello world!!! "
Создадим новый экземпляр того же объекта:
(define Obj2 (MyObject " Hello " " habra!!! ")) ; ; результат: "Hello Hello habra!!! world!!!"
Попробуем выполнить:
(display ((Obj1 'get-f1))) (display ((Obj1 'get-f2))) (newline) (display ((Obj2 'get-f1))) (display ((Obj2 'get-f2))) (newline) ; результатом будет "Hello world!!!" ; и "Hello habra!!!"
Что меня поразило — каждый экземпляр возвращает одни и теже функции, но их поведение не одинаковое.
То есть в Scheme, в отличие от С++, функции берут переменные и функции для вычисления из своего экземпляра.
Данная особенность сильно помогает организации списков функций для изменения внутренних состояний разных экземпляров одного или нескольких объектов:
(define func-list (list (Obj1 'get-f1) (Obj2 'get-f1) (Obj2 'get-f2) (Obj1 'get-f2)))
Распечатаем выполнения каждой функции из этого списка так:
(map (lambda (x) (display (x))) func-list)
; результат: «Hello Hello habra!!! world!!!»