-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy path04.09.1 使用赋值的指导原则
112 lines (78 loc) · 2.41 KB
/
04.09.1 使用赋值的指导原则
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
4.9.1 使用赋值的指导原则
虽然使用set!有时是适当的,Racket风格通常建议不使用set!。下面的准则有助于解释什么时候使用set!是适当的。
1、与任何现代语言一样,分配给共享标识符不是将参数传递给过程或获得结果的替代。
事实上很糟糕的示例:
(define name "unknown")
(define result "unknown")
(define (greet)
(set! result (string-append "Hello, " name)))
> (set! name "John")
> (greet)
> result
"Hello, John"
合适的示例:
(define (greet name)
(string-append "Hello, " name))
> (greet "John")
"Hello, John"
> (greet "Anna")
"Hello, Anna"
2、对局部变量的赋值序列远比嵌套绑定差。
差的示例:
> (let ([tree 0])
(set! tree (list tree 1 tree))
(set! tree (list tree 2 tree))
(set! tree (list tree 3 tree))
tree)
'(((0 1 0) 2 (0 1 0)) 3 ((0 1 0) 2 (0 1 0)))
好的示例:
> (let* ([tree 0]
[tree (list tree 1 tree)]
[tree (list tree 2 tree)]
[tree (list tree 3 tree)])
tree)
'(((0 1 0) 2 (0 1 0)) 3 ((0 1 0) 2 (0 1 0)))
3、使用赋值来从迭代中积累结果是不好的风格。通过循环参数积累更好。
略差的示例:
(define (sum lst)
(let ([s 0])
(for-each (lambda (i) (set! s (+ i s)))
lst)
s))
> (sum '(1 2 3))
6
好的示例:
(define (sum lst)
(let loop ([lst lst] [s 0])
(if (null? lst)
s
(loop (cdr lst) (+ s (car lst))))))
> (sum '(1 2 3))
6
更好(使用现有函数)示例:
(define (sum lst)
(apply + lst))
> (sum '(1 2 3))
6
好的(一般的途径)示例:
(define (sum lst)
(for/fold ([s 0])
([i (in-list lst)])
(+ s i)))
> (sum '(1 2 3))
6
4、对于有状态的对象的情况是必要的或合适的,那么用set!实现对象的状态是比较好的。
合适的实例:
(define next-number!
(let ([n 0])
(lambda ()
(set! n (add1 n))
n)))
> (next-number!)
1
> (next-number!)
2
> (next-number!)
3
所有其它的情况都相同,则不使用赋值或变化的程序总是优于使用赋值或变化的程序。虽然应该避免副作用,但如果结果代码可读性更高,或者实现了更好的算法,则应该使用这些副作用。
使用可变值,如向量和哈希表,对程序风格的疑虑会比直接使用set!要少。不过,在程序里简单地用vector-set!替换set!显然没有改善程序的风格。