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

в 1:09, , рубрики: Программирование

Привет. В данной статье хотелось бы еще разок осветить вопрос объектного программирования на языке 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 fild1 fild2) ;  объект у которого при конструировании будут две переменные fild1 и fild2  
    (define f1 fild1) ; инициализация внутренней переменной f1, с помощью fild1
    (define f2 fild2) ; ...
    (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!!!"

Автор: javamain


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js