Pull to refresh

Объектно ориентированный подход на функциях в Scheme

Reading time3 min
Views4.3K
imageПривет. В данной статье хотелось бы еще разок осветить вопрос объектного программирования на языке 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!!!»
Tags:
Hubs:
+16
Comments7

Articles