Загрузка данных


% Лабораторная работа № 2. Типы данных. Модульное тестирование
% <лабораторная ещё не сдана>
% Кирилл Меньшиков, ИУ9-12/6

# Цель работы

На практике ознакомиться с системой типов языка Scheme.
На практике ознакомиться с юнит-тестированием.
Разработать свои средства отладки программ на языке Scheme.
На практике ознакомиться со средствами метапрограммирования языка Scheme.

# Индивидуальный вариант

Процедура `reverse!` — деструктивное обращение списка, вектора или строки (с необязательным приёмником).
Процедура `factorize` — разложение на множители выражений вида `a² − b²`, `a³ − b³`, `a³ + b³`.

Также реализованы макрос `trace`, макрос `test` и процедуры `run-test`, `run-tests`.

# Реализация

```scheme
;; trace.scm
(define-syntax trace
  (syntax-rules ()
    ((trace expr)
     (begin
       (write 'expr)
       (display " => ")
       (let ((value expr))
         (display value)
         (newline)
         value)))))
```

```scheme
;; unit-test.scm
(define-syntax test
  (syntax-rules ()
    ((test expr expected)
     (list 'expr (lambda () expr) expected))))

(define (run-test t)
  (let ((expr (car t))
        (thunk (cadr t))
        (expected (caddr t)))
    (write expr)
    (let ((actual (thunk)))
      (if (equal? actual expected)
          (begin (display " ok") (newline) #t)
          (begin
            (display " FAIL")
            (newline)
            (display "  Expected: ")
            (write expected)
            (newline)
            (display "  Returned: ")
            (write actual)
            (newline)
            #f)))))

(define (run-tests tests)
  (let loop ((rest tests) (all-ok #t))
    (if (null? rest)
        all-ok
        (let ((ok (run-test (car rest))))
          (loop (cdr rest) (and all-ok ok))))))
```

```scheme
;; reverse!
(define (reverse! seq . opt)
  (define (size x)
    (cond ((vector? x) (vector-length x))
          ((string? x) (string-length x))
          (else (length x))))
  (define (ref x i)
    (cond ((vector? x) (vector-ref x i))
          ((string? x) (string-ref x i))
          (else (list-ref x i))))
  (define (put! x i v)
    (cond ((vector? x) (vector-set! x i v))
          ((string? x) (string-set! x i v))
          (else (set-car! (list-tail x i) v))))

  (let* ((dest (if (null? opt) seq (car opt)))
         (n (size seq)))
    (let loop ((i 0))
      (if (<= i (- n i 1))
          (let ((left (ref seq i))
                (right (ref seq (- n i 1))))
            (put! dest i right)
            (put! dest (- n i 1) left)
            (loop (+ i 1)))))
    dest))
```

```scheme
;; factorize
(define (factorize expr)
  (define (power-term? x)
    (and (pair? x)
         (eq? (car x) 'expt)
         (= (length x) 3)
         (number? (caddr x))
         (or (= (caddr x) 2) (= (caddr x) 3))))
  (define (base x) (cadr x))
  (define (degree x) (caddr x))

  (if (and (pair? expr)
           (= (length expr) 3)
           (or (eq? (car expr) '-) (eq? (car expr) '+))
           (power-term? (cadr expr))
           (power-term? (caddr expr))
           (= (degree (cadr expr)) (degree (caddr expr))))
      (let ((op (car expr))
            (a (base (cadr expr)))
            (b (base (caddr expr)))
            (n (degree (cadr expr))))
        (cond
          ((and (eq? op '-) (= n 2))
           `(* (- ,a ,b) (+ ,a ,b)))
          ((and (eq? op '-) (= n 3))
           `(* (- ,a ,b) (+ (expt ,a 2) (* ,a ,b) (expt ,b 2))))
          ((and (eq? op '+) (= n 3))
           `(* (+ ,a ,b) (+ (- (expt ,a 2) (* ,a ,b)) (expt ,b 2))))
          (else expr)))
      expr))
```

# Тестирование

```
Welcome to DrRacket, version 6.10.1 [3m].
Language: R5RS; memory limit: 128 MB.
> (define (zip . xss)
    (if (or (null? xss)
            (null? (trace (car xss))))
        '()
        (cons (map car xss)
              (apply zip (map cdr (trace xss))))))
> (zip '(1 2 3) '(one two three))
(car xss) => (1 2 3)
xss => ((1 2 3) (one two three))
(car xss) => (2 3)
xss => ((2 3) (two three))
(car xss) => (3)
xss => ((3) (three))
(car xss) => ()
((1 one) (2 two) (3 three))
> (define counter
    (let ((n 0))
      (lambda () (set! n (+ n 1)) n)))
> (+ (trace (counter)) (trace (counter)))
(counter) => 1
(counter) => 2
3
```

```scheme
(define (signum x)
  (cond ((< x 0) -1)
        ((= x 0)  1) ; ошибка здесь
        (else     1)))

(define counter
  (let ((n 0))
    (lambda () (set! n (+ n 1)) n)))

(define the-tests
  (list (test (signum -2) -1)
        (test (signum  0)  0)
        (test (signum  2)  1)
        (test (counter) 1)
        (test (counter) 77) ; ошибка в тесте, должно быть 2
        (test (counter) 3)
        (test (* 7 7) "49")
        (test (string-append "4" "9") 49)))
```

```
Welcome to DrRacket, version 6.10.1 [3m].
Language: R5RS; memory limit: 128 MB.
> (run-tests the-tests)
(signum -2) ok
(signum 0) FAIL
  Expected: 0
  Returned: 1
(signum 2) ok
(counter) ok
(counter) FAIL
  Expected: 77
  Returned: 2
(counter) ok
(* 7 7) FAIL
  Expected: "49"
  Returned: 49
(string-append "4" "9") FAIL
  Expected: 49
  Returned: "49"
#f
> (define bad-test (test (/ 10 0) 100))
> (run-test bad-test)
(/ 10 0)
/: division by zero
```

```scheme
(define (shared)
  (let* ((as (list 'a 'b 'c))
         (bs (append (list 'x 'y 'z) as)))
    (set-car! as 1)
    (list as bs)))

(define reverse-tests
  (list
   (test (let ((xs (list 1 2 3 4 5))) (reverse! xs) xs)
         '(5 4 3 2 1))
   (test (let ((xs (list 1 2 3 4))) (reverse! xs) xs)
         '(4 3 2 1))
   (test (let ((xs (list))) (reverse! xs) xs)
         '())
   (test (let ((ys (vector 1 2 3 4 5))) (reverse! ys) ys)
         '#(5 4 3 2 1))
   (test (let ((zs (string #\h #\e #\l #\l #\o))) (reverse! zs) zs)
         "olleh")
   (test (let ((xs (list 1 2 3))) (reverse! xs xs) xs)
         '(3 2 1))
   (test (let ((x (list 1 2 3)) (y (vector 0 0 0)))
           (reverse! x y)
           (list x y))
         '((1 2 3) #(3 2 1)))
   (test (let ((s (string #\a #\b)) (l (list 0 0)))
           (reverse! s l)
           (list s l))
         '("ab" (#\b #\a)))
   (test (let ((p (shared)))
           (reverse! (cadr p))
           p)
         '((z y x) (c b 1 z y x)))
   (test (let ((bs (cadr (shared))))
           (reverse! bs)
           (reverse! (cdr bs))
           bs)
         '(c x y z 1 b))))
```

```
Welcome to DrRacket, version 6.10.1 [3m].
Language: R5RS; memory limit: 128 MB.
> (run-tests reverse-tests)
(let ((xs (list 1 2 3 4 5))) (reverse! xs) xs) ok
(let ((xs (list 1 2 3 4))) (reverse! xs) xs) ok
(let ((xs (list))) (reverse! xs) xs) ok
(let ((ys (vector 1 2 3 4 5))) (reverse! ys) ys) ok
(let ((zs (string #\h #\e #\l #\l #\o))) (reverse! zs) zs) ok
(let ((xs (list 1 2 3))) (reverse! xs xs) xs) ok
(let ((x (list 1 2 3)) (y (vector 0 0 0))) (reverse! x y) (list x y)) ok
(let ((s (string #\a #\b)) (l (list 0 0))) (reverse! s l) (list s l)) ok
(let ((p (shared))) (reverse! (cadr p)) p) ok
(let ((bs (cadr (shared)))) (reverse! bs) (reverse! (cdr bs)) bs) ok
#t
```

```scheme
(define (value-of expr a b)
  (eval (list (list 'lambda '(x y) expr) a b)
        (interaction-environment)))

(define factorize-tests
  (list
   (test (factorize '(- (expt x 2) (expt y 2)))
         '(* (- x y) (+ x y)))
   (test (factorize '(- (expt x 3) (expt y 3)))
         '(* (- x y) (+ (expt x 2) (* x y) (expt y 2))))
   (test (factorize '(+ (expt x 3) (expt y 3)))
         '(* (+ x y) (+ (- (expt x 2) (* x y)) (expt y 2))))
   (test (factorize '(- (expt (+ first 1) 2) (expt (- second 1) 2)))
         '(* (- (+ first 1) (- second 1))
             (+ (+ first 1) (- second 1))))
   (test (factorize '(+ (expt x 2) (expt y 2)))
         '(+ (expt x 2) (expt y 2)))
   (test (factorize '(* x y)) '(* x y))
   (test (factorize 5) 5)
   (test (value-of (factorize '(- (expt x 2) (expt y 2))) 1 2) -3)
   (test (value-of (factorize '(- (expt x 3) (expt y 3))) 3 2) 19)
   (test (value-of (factorize '(+ (expt x 3) (expt y 3))) 3 2) 35)))
```

```
Welcome to DrRacket, version 6.10.1 [3m].
Language: R5RS; memory limit: 128 MB.
> (run-tests factorize-tests)
(factorize (quote (- (expt x 2) (expt y 2)))) ok
(factorize (quote (- (expt x 3) (expt y 3)))) ok
(factorize (quote (+ (expt x 3) (expt y 3)))) ok
(factorize (quote (- (expt (+ first 1) 2) (expt (- second 1) 2)))) ok
(factorize (quote (+ (expt x 2) (expt y 2)))) ok
(factorize (quote (* x y))) ok
(factorize 5) ok
(value-of (factorize (quote (- (expt x 2) (expt y 2)))) 1 2) ok
(value-of (factorize (quote (- (expt x 3) (expt y 3)))) 3 2) ok
(value-of (factorize (quote (+ (expt x 3) (expt y 3)))) 3 2) ok
#t
> (eval (list (list 'lambda
                    '(x y)
                    (factorize '(- (expt x 2) (expt y 2))))
              1 2)
        (interaction-environment))
-3
```

# Вывод

В ходе лабораторной работы я познакомился с системой типов Scheme, макросами и метапрограммированием. Реализовал макрос `trace` для трассировки и каркас модульного тестирования, в котором тест не вычисляется при создании, а результаты сравниваются с помощью `equal?`. Реализовал процедуры `reverse!` для обращения списков, векторов и строк и `factorize` для разложения выражений на множители, проверил их модульными тестами.