芯有所想

精益求精

Monad初探

之前碰过一段时间的Scheme/Haskell,觉得函数式编程思想让人开拓了思路。不过对于其中的Monad始终不明白,在BBS上搜到一篇文章,通过一个实例让我了解了一点点,现在将程序贴出来。

Monad就像一个链式反应,返回的Monad作为下一个Monad函数的参数输入和状态输入。

使用的是Scheme语言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
(define (make-tagged-value tag value)
  (cons tag value))

(define (get-tag x) (car x))

(define (get-value x) (cdr x))

;; type : a -> TaggedM a
(define (return a)
  (lambda (tag) (make-tagged-value tag a)))

;; >>= bind函数,接受一个monad,以及一个函数a -> TaggedM b,
;; 返回一个新的Monad: Taggged b
(define (>>= m f) ; m is Monad a, f is a->Monad b
  (lambda (tag)
    (let* ( (m-result (m tag))
            (tag (get-tag m-result))
            (value (get-value m-result))
            (new-m (f value)) )
      (new-m tag))))

;; inc用来封装变化
;; type : int -> TaggedM int
(define (inc n)
  (make-tagged-value (+ n 1) n))

(define (make-node value lchild rchild)
  (>>= inc
       (lambda (x)
         (return (list (make-tagged-value x value) lchild rchild)) ) ) )

(define (make-tree-left-first depth)
  (if (= depth 0)
      (make-node 0 '() '())
      (>>= (make-tree-left-first (- depth 1))
           (lambda (lchild)
             (>>= (make-tree-left-first (- depth 1))
                  (lambda (rchild)
                    (make-node depth lchild rchild)))))))


(define (make-tree-right-first depth)
  (if (= depth 0)
      (make-node 0 '() '())
      (>>= (make-tree-right-first (- depth 1))
           (lambda (rchild)
             (>>= (make-tree-right-first (- depth 1))
                  (lambda (lchild)
                    (make-node depth lchild rchild)))))))

(define left-first-tree-monad (make-tree-left-first 3))
(define right-first-tree-monad (make-tree-right-first 3))


(define test-left
  (>>= (make-node 0 '() '())
       (lambda (l)
         (>>= (make-node 0 '() '())
              (lambda (r)
                (make-node 1 l r)))) ) 
  )
(define test-right
  (>>= (make-node 0 '() '())
       (lambda (r)
         (>>= (make-node 0 '() '())
              (lambda (l)
                (make-node 1 l r)))) ) 
  )