响应式编程

响应式编程(Reactive Programming),算是一种异步编程范式。简而言之,在传统指令式编程下:

a = 1
b = 2
c = a + b 
print c //输出 3
a = 4
print c //输出 3

这个例子很平淡,但是在响应式下:

a = 1
b = 2
c = a + b 
print c //输出 3
a = 4
print c //输出 6
b = 3
print c //输出 7

实现方式

这里引发了一个问题,假设只考虑上例的应用场景,怎么实现这种语义?首相,你完全可以重新设计一门响应式语言。但要如何在流行的指令式语言中实现这种特性,思考了一下,这门语言至少得支持操作符重载。先来看C++:


//以下代码仅作为演示,不完全符合规范
class Num {
public:
  vector<Num *> operator+(Num &rhs) {
    this->handler_.push_back(&rhs);
    return this->handler_;
  }

  void operator=(int n) { this->x_ = n; }

  vector<Num *> operator=(vector<Num *> res) {
    this->handler_ = res;
    return this->handler_;
  }

  int val() {
    int accu = 0;
    for (Num *n : handler_) {
      accu += n->x_;
    }
    this->x_ = accu;
    return accu;
  }

  Num(vector<Num *> handler) : handler_(handler) {}

  Num(int n) : x_(n) { this->handler_.push_back(this); }

private:
  int x_;
  vector<Num *> handler_;
};

ostream &operator<<(ostream &os, Num &num) {
  os << num.val();
  return os;
}
Num a = 1;
Num b = 2;
Num c = a + b;
cout << c; //输出 3
a = 3;
cout << c; //输出 5

这是我能想到最暴力的方法(这里只实现了最基本的语义,没有考虑复杂的逻辑),是不是很像反的观察者模式(Observer Pattern)。那么,有没有更简单的方法?

经过思考,这里还可以用lambda表达式勉强地实现:

auto a = 1;
auto b = 2;
auto c = [&]() { return a + b; };
a = 3;
cout << c(); //输出 5

这里虽然语义不完全相同,但是简化代码的同时,能起到类似的效果。只要语言支持lambda表达式或者匿名函数都可以这样做:

//Go
a := 1
b := 2
c := func(){
  return a + b
}
//Javascript
let a = 1
let b = 2
const c = () => { return a + b;}

Java的变量捕获非常恶心,不适合这么搞。但是,Java的亲戚Scala大量的语法糖,是否可以更加简化代码?

var a = 1
var b = 2
var c = () => {
  a + b
}
a = 10
println(c()) //输出 12

结构上没有问题,不过代码貌似没怎么简化?传说中,Scala每重构一次性能和形式上都会有大的提升,那就试试重构:

var a = 1
var b = 2
def c = a + b

现在看起来,确实形式上非常接近,而且代码非常简单。并且只需要这样:

var a = 3
println(c) //输出 5

Scala定义的函数,调用时居然可以不写括号,真是简洁又邪恶。

事实上,这些例子勉强扯上了响应器模式(Reactor Pattern)。并且,函数式的流行使原本要用特殊技巧解决的问题得到了简化。这里可以展开讲一些有趣的事,留到后续的博客。

Leave a Reply

Your email address will not be published. Required fields are marked *