函数式编程(二)

复习几个有用的概念

Delay

delay用来延迟求值

1
2
3
4
5
6
7
;延迟执行
(def d (delay (println "Running...")
:done!))
(deref d)
;第二次不会执行println, 会直接使用缓存的结果
(deref d)

Future

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
;future在另一个线程中执行它所包含的代码
(def long-calculation (future (apply + (range 1e7)))) ;改成1e9看看变化
(deref long-calculation)
;结果有什么不同
(deref (future (Thread/sleep 5000) :done!)
1000
:impatient) ;:impatient
(deref (future (Thread/sleep 5000) :done!)
6000
:impatient);:done!
;如果e总是有用的,那么future能带来吞吐量的提升
(def e (future (println "Running...")
:done!))
(deref e)
(deref e)
;可以组合起来
(def d (delay (println "Running ...") :done!))
(def e (future (deref d)))
(deref e) ;:done

Promise

1
2
3
4
5
6
7
8
9
10
11
12
;创造的时候并不会执行代码,promise可以用于不同线程之间的通信和协调
(def a (promise))
(def b (promise))
(def c (promise))
(future
(deliver c (+ @a @b))
(println "delivery complete!"))
(deliver a 10)
; a,b都有值时,deliver c才会执行
(deliver b 15)

宏是用来给语言添加新的结构,新的元素的。它们是一些在读入期(而不是编译期)就会实际代码替换的一个机制。

对于函数来说,它们的所有的参数都会被evaluate的, 而宏则会自动判断哪些参数需要evaluate。 这对于实现像(if condition then-expr else-expr)这样的结构是非常重要的。 如果 condition 是true, 那么只有 “then” 表达式需要被evaluated. 如果条件是false, 那么只有 “else” 表达式应该被 evaluated. 这意味着if 不能被实现成一个函数。

假设我们代码里面很多地方要对一个数字进行判断,通过判断它是接近0, 是正的, 是负的来执行不同的逻辑;我们又不想这种判断的代码到处重复,那么这种情况下我们就可以使用宏了。我们使用defmacro 宏来定义一个宏。

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
;number# 在宏里面用来产生唯一的symbol名字
;宏定义开始的时候的那个反引号 (也称为语法引号) 防止宏体内的任何一个表达式被evaluate
;当一个名字前面被加了一个波浪号,并且还在反引号里面,它的值会被替换的
;
(defmacro around-zero [number negative-expr zero-expr positive-expr]
`(let [number# ~number] ; so number is only evaluated once
(cond
(< (Math/abs number#) 1e-15) ~zero-expr
(pos? number#) ~positive-expr
true ~negative-expr)))
;example1
(around-zero 0.1 (println "-") (println "0") (println "+"))
;example2
(println (around-zero 0.1 "-" "0" "+"))
;实现my-if
(defmacro my-if [condition positive-expr negative-expr]
`(cond ~condition ~positive-expr
true ~negative-expr))
(defn test-myif [x]
(my-if (> x 0)
x
0))
(test-myif 3)
(test-myif -1)
;验证这个宏是否被正确展开
(macroexpand-1
'(around-zero 0.1 (println "-") (println "0") (println "+")))
(macroexpand-1
'(my-if (> x 0)
x
0))

clojure并发

协调:涉及到多个角色,操作后,多个角色的状态都是正确的结果。例如转账,涉及到两个账号,转出账号与转入账号在转账前后的和相等。

- coordinated uncoordinated
sync Refs Atoms
async - Agents

atom

类似于Java中的原子变量(def a (atom 0))

reset!

直接修改atom的值

1
(reset! a 1)

compare-and-set!

swap!

先比较旧值是否匹配,然后设定新值,如果原子类型的值在你的更新函数返回之前发生了变化,那么swap!会自动进行重试。所以swap!传入的函数可能不止执行一次,所以需要没有副作用。

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
;Usage:
(swap! atom f x y & args)
(defmacro futures
[n & exprs]
(vec (for [_ (range n)
expr exprs]
`(future ~expr))))
(defmacro wait-futures
[& args]
`(doseq [f# (futures ~@args)]
@f#))
;示例
(wait-futures 1 (swap! xs (fn [v]
(Thread/sleep 250)
(println "trying 4")
(conj v 4)))
(swap! xs (fn [v]
(Thread/sleep 500)
(println "trying 5")
(conj v 5))))
;trying 4
;trying 5
;trying 5

agent

agent是一种不进行协调的、异步的引用类型。agent的修改是在其他线程中进行的,如果有多个线程同时调用send,传给send的函数将被串行调用,同一时间只会调用一个,不会重试。

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
;定义
(def my-agent (agent 0))
(println @my-agent)
(send my-agent inc)
(println @my-agent)
;延迟,查看异步执行
(defn inc-with-sleep [x]
(Thread/sleep 5000)
(inc x))
(send my-agent inc-with-sleep)
(println @my-agent)
;示例,打印Log
(import 'java.util.Date 'java.text.SimpleDateFormat)
(defn now []
(.format (SimpleDateFormat. "yyyy-MM-dd HH:mm:ss") (Date.)))
(println (now))
(def log-entries (agent []))
(defn log [entry]
(send log-entries conj [(now) entry]))
(log "sth. happened")
(log "sth. else happened")
(println @log-entries)

ref

  • ref可以协调,实现STM(软事务内存),atom和agent每次只能修改一个变量,通过STM可以实现多个变量并发一致的修改。
  • STM运行时检测并发冲突,发现冲突后重试
    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
    ;单个引用
    (def my-ref (ref 0))
    ;(alter my-ref inc)
    ;使用dosync包裹事务
    (dosync (alter my-ref inc))
    (eval @my-ref)
    ;多个引用
    (dosync (alter my-ref inc))
    (eval @my-ref)
    (def me (ref 1000))
    (def jiaoshou (ref 2000))
    (defn transfer [from to amount]
    (dosync
    (alter from - amount)
    (alter to + amount)))
    (transfer jiaoshou me 500)
    (eval @me)
    (eval @jiaoshou)
    (transfer jiaoshou me 1300)
    (eval @me)
    (eval @jiaoshou)
    ;stress
    (def attempts (atom 0))
    (def transfers (agent 0))
    (defn transfer [from to amount]
    (dosync
    (swap! attempts inc) ; Side-effect in transaction - DON'T DO THIS
    (send transfers inc)
    (alter from - amount)
    (alter to + amount)))
    (def me2 (ref 10000))
    (def jiaoshou2 (ref 20000))
    (defn stress-thread [from to iterations amount]
    (Thread. #(dotimes [_ iterations] (transfer from to amount))))
    (defn gogogo [& args]
    (println "Before: Me =" @me2 " Jiaoshou =" @jiaoshou2)
    (let [t1 (stress-thread me2 jiaoshou2 100 100)
    t2 (stress-thread jiaoshou2 me2 200 100)]
    (.start t1)
    (.start t2)
    (.join t1)
    (.join t2))
    (await transfers)
    (println "Attempts: " @attempts)
    (println "Transfers: " @transfers)
    (println "After: Me =" @me2 " Jiaoshou =" @jiaoshou2))
    (gogogo)

示例:哲学家就餐

1

Java

会导致死锁的实现

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
public class PhilosophersExample1 {
public static class Philosopher extends Thread {
private int id;
private Chopstick left;
private Chopstick right;
private Random random;
public Philosopher(int id, Chopstick left, Chopstick right) {
this.id = id;
this.left = left;
this.right = right;
random = new Random();
}
public void think() {
System.out.println("ID: " + id + " Philosopher is thinking" );
}
public void eat() {
System.out.println("ID: " + id + " Philosopher is eating" );
}
@Override
public void run() {
System.out.println("ID: " + id + " Philosopher is begin");
try {
while (true) {
Thread.sleep(random.nextInt(3000));
think();
synchronized (left) {
synchronized (right) {
Thread.sleep(random.nextInt(5000));
eat();
}
}
}
} catch (InterruptedException e) {
}
}
public static void main(String[] args) throws InterruptedException {
Philosopher[] philosophers = new Philosopher[5];
Chopstick[] chopsticks = new Chopstick[5];
for (int i = 0; i < 5; ++i) {
chopsticks[i] = new Chopstick(i);
}
for (int i = 0; i < 5; i++) {
philosophers[i] = new Philosopher(i, chopsticks[i],
chopsticks[(i + 1) % 5]);
philosophers[i].start();
}
for(int i = 0; i < 5; ++i) {
philosophers[i].join();
}
System.out.println("end");
}
public static class Chopstick {
private int id;
public Chopstick(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
}
}

设置优先级

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
public class PhilosophersExample2 {
public static class Philosopher extends Thread {
private int id;
private Chopstick first;
private Chopstick second;
private Random random;
public Philosopher(int id, Chopstick left, Chopstick right) {
this.id = id;
if(left.getId() < right.getId()) {
this.first = left;
this.second = right;
}else {
this.second = left;
this.first = right;
}
random = new Random();
}
public void think() {
System.out.println("ID: " + id + " Philosopher is thinking" );
}
public void eat() {
System.out.println("ID: " + id + " Philosopher is eating" );
}
@Override
public void run() {
System.out.println("ID: " + id + " Philosopher is begin");
try {
while (true) {
Thread.sleep(random.nextInt(3000));
think();
synchronized (first) {
synchronized (second) {
Thread.sleep(random.nextInt(5000));
eat();
}
}
}
} catch (InterruptedException e) {
}
}
public static void main(String[] args) throws InterruptedException {
Philosopher[] philosophers = new Philosopher[5];
Chopstick[] chopsticks = new Chopstick[5];
for (int i = 0; i < 5; ++i) {
chopsticks[i] = new Chopstick(i);
}
for (int i = 0; i < 5; i++) {
philosophers[i] = new Philosopher(i, chopsticks[i],
chopsticks[(i + 1) % 5]);
philosophers[i].start();
}
for(int i = 0; i < 5; ++i) {
philosophers[i].join();
}
System.out.println("end");
}
public static class Chopstick {
private int id;
public Chopstick(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
}
}

资源撤销

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
public class PhilosophersExample3 {
public static class Philosopher extends Thread {
private int id;
private Chopstick left;
private Chopstick right;
private Random random;
private ReentrantLock leftChopstickLock;
private ReentrantLock rightChopstickLock;
public Philosopher(int id, Chopstick left, Chopstick right,
ReentrantLock leftLock, ReentrantLock rightLock) {
this.id = id;
this.left = left;
this.right = right;
this.leftChopstickLock = leftLock;
this.rightChopstickLock = rightLock;
random = new Random();
}
public void think() {
System.out.println("ID: " + id + " Philosopher is thinking");
}
public void eat() {
System.out.println("ID: " + id + " Philosopher is eating");
}
@Override
public void run() {
System.out.println("ID: " + id + " Philosopher is begin");
try {
while (true) {
Thread.sleep(random.nextInt(3000));
think();
leftChopstickLock.lock();
try {
if (rightChopstickLock.tryLock(1000,
TimeUnit.MILLISECONDS)) {
Thread.sleep(random.nextInt(5000));
eat();
}
} finally {
leftChopstickLock.unlock();
}
}
} catch (InterruptedException e) {
}
}
public static void main(String[] args) throws InterruptedException {
Philosopher[] philosophers = new Philosopher[5];
Chopstick[] chopsticks = new Chopstick[5];
ReentrantLock[] chopstickLocks = new ReentrantLock[5];
for (int i = 0; i < 5; ++i) {
chopsticks[i] = new Chopstick(i);
}
for (int i = 0; i < 5; ++i) {
chopstickLocks[i] = new ReentrantLock();
}
for (int i = 0; i < 5; i++) {
philosophers[i] = new Philosopher(i, chopsticks[i],
chopsticks[(i + 1) % 5], chopstickLocks[i], chopstickLocks[(i + 1) % 5]);
philosophers[i].start();
}
for (int i = 0; i < 5; ++i) {
philosophers[i].join();
}
System.out.println("end");
}
public static class Chopstick {
private int id;
public Chopstick(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
}
}

确定能执行时再持有资源

  • 给table加锁,而不给筷子加锁
  • 当哲学家饥饿时,锁住餐桌,这样其他哲学家无法改变状态。然后查看左右是否正在进餐,如果没有,那么该哲学家开始进餐并解锁餐桌。否则,调用await()并解锁餐桌。
  • 当哲学家吃完后开始思考时,锁住餐桌,改变自己的状态,通过左右可以进餐,然后解锁餐桌。
    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
    public class PhilosophersExample4 {
    public static class Philosopher extends Thread {
    private boolean eating;
    private Philosopher left;
    private Philosopher right;
    private ReentrantLock table;
    private Condition condition;
    private Random random;
    private int thinkCount;
    public Philosopher(ReentrantLock table) {
    eating = false;
    this.table = table;
    condition = table.newCondition();
    random = new Random();
    }
    public void setLeft(Philosopher left) {
    this.left = left;
    }
    public void setRight(Philosopher right) {
    this.right = right;
    }
    public void run() {
    try {
    while (true) {
    think();
    eat();
    }
    } catch (InterruptedException e) {
    }
    }
    private void think() throws InterruptedException {
    table.lock();
    try {
    eating = false;
    left.condition.signal();
    right.condition.signal();
    } finally {
    table.unlock();
    }
    ++thinkCount;
    if (thinkCount % 10 == 0)
    System.out.println("Philosopher " + this + " has thought "
    + thinkCount + " times");
    Thread.sleep(1000);
    }
    private void eat() throws InterruptedException {
    table.lock();
    try {
    while (left.eating || right.eating)
    condition.await();
    eating = true;
    } finally {
    table.unlock();
    }
    Thread.sleep(1000);
    }
    }
    public static void main(String[] args) throws InterruptedException {
    Philosopher[] philosophers = new Philosopher[5];
    ReentrantLock table = new ReentrantLock();
    for (int i = 0; i < 5; ++i) {
    philosophers[i] = new Philosopher(table);
    }
    for (int i = 0; i < 5; ++i) {
    philosophers[i].setLeft(philosophers[(i + 4) % 5]);
    philosophers[i].setRight(philosophers[(i + 1) % 5]);
    philosophers[i].start();
    }
    for (int i = 0; i < 5; ++i) {
    philosophers[i].join();
    }
    }
    }

clojure STM

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
(def philosophers (into [] (repeatedly 5 #(ref :thinking))))
(defn claim-chopsticks [philosopher left right]
(dosync
(when (and (= (ensure left) :thinking) (= (ensure right) :thinking))
(ref-set philosopher :eating))))
(defn release-chopsticks [philosopher]
(dosync (ref-set philosopher :thinking)))
(defn think []
(Thread/sleep (rand 1000)))
(defn eat []
(Thread/sleep (rand 1000)))
(defn philosopher-thread [n]
(Thread.
#(let [philosopher (philosophers n)
left (philosophers (mod (- n 1) 5))
right (philosophers (mod (+ n 1) 5))]
(while true
(think)
(when (claim-chopsticks philosopher left right)
(eat)
(release-chopsticks philosopher))))))

luminus web 框架

hello world

lein new luminus my-app
cd my-app
lein run

简单的程序

curl -d "name=john" "http://localhost:3000/player"
curl -d "name=george" "http://localhost:3000/player"
curl -d "name=ringo" "http://localhost:3000/player"
curl http://localhost:3000/players