我最近正在阅读有关游戏引擎设计的内容,并最终绊倒了这个:What is (functional) reactive programming?
我想知道如何实现第二高评级答案中给出的示例。在C ++中,很容易将指针传递给存储鼠标坐标的值,只返回值而不是int。好吧,我们不能在C#中真正做到这一点,所以这是我们的第一个问题。我们是否需要调用一些“更新”功能来保持所有值的最新状态?
其次如何处理语法?分配值是直截了当的。但是,每当我要求它时,“让鼠标定位并从中取出14分钟”会稍微复杂一些......
最后,我想知道如何直接引用C#中的任何对象返回值。例如
int test = 1;
测试将返回1.所以我可以做1 + test之类的事情,这将是= 2
但如果我有一个
的实例public class ReactiveInt {
int myValue
}
在尝试添加int时,我不能只做我上面做的事。
对不起我想这么广泛的问题。如果可以给出一个简单的例子,演示类似于答案中讨论的功能,我想我的所有问题都会得到解答..
答案 0 :(得分:8)
问题1
在尝试添加int时,我不能只做我上面做的事。
嗯,这实际上就是重点。在反应式编程中,您不希望强制性地将两个数字相加,而是希望根据其他数字定义新数字。因此,即使c = a + b
或c
发生变化,a + b
,a
总是等于b
:c
}对a
和b
具有反应性。
var a = new BehaviorSubject(3);
var b = new BehaviorSubject(1);
var c = Rx.Observable.combineLatest(a, b, function(vals) {
return vals[0] + vals[1];
});
问题2
我想知道如何实现第二个最高评级答案中给出的例子。
最简单的答案列表和haskell中的高阶函数。
回答你不想要功能反应式编程违背你在命令式编程中学到的一切,你将不得不重新学习如何做如果你想做纯粹的功能反应式编程,那就是事情。如果你不这样做,你最终会制作各种依赖性跟踪库,例如KnockoutJS,当你可以在几百行中使用RxJS-Splash之类的东西做同样的事情,如果你使用FRP开始。 (注意Splash是如何基于Rx的,它是可重用的代码,而Knockout是纯粹的特定于实现的代码)。
FRP具有事件和时间的概念,而依赖关系跟踪仅包含值和更改的概念。功能反应代码与命令式代码一样长。它不是“建立在命令式代码之上”。 (是的,它仍然汇编到汇编...而不是重点),它的概念从根本上不同。
使用Microsoft的JavaScript Reactive Extensions(RxJS)
请注意,Rx现在提供多种语言版本,包括原生C++。
示例的直接端口
var moves = $(document).onAsObservable('mousemove')
.map(function(e) {
return {
x: e.pageX,
y: e.pageY
};
});
var xs = moves.map(function(move) { return move.x; });
var ys = moves.map(function(move) { return move.y; });
var minXs = xs.map(function(x) { return x - 16; });
var minYs = ys.map(function(y) { return y - 16; });
var maxYs = xs.map(function(x) { return x + 16; });
var maxYs = ys.map(function(y) { return y + 16; });
var boundingRect = Rx.Observable.combineLatest(minXs, minYs, maxXs, maxYs)
.map(function(vals) {
var minX = vals[0];
var minY = vals[1];
var maxX = vals[2];
var maxY = vals[3];
return new Rectangle(minX, minY, maxX, maxY);
});
简化端口
由于矩形仅根据一个相关值(或事件)定义,因此您可以将其简化为以下内容:
var boundingRect = $(document).onAsObservable('mousemove')
.map(function(e) {
var x = e.pageX;
var y = e.pageY;
return new Rectangle(x - 16, y - 16, x + 16, y + 16);
});
使用
此时您可以使用它来组成其他可观察序列(随时间变化的值)。
var area = boundingRect.map(function(rect) {
return rect.getSize();
});
或直接订阅。
boundingRect.subscribe(function (rect) {
// perform some action with the rect each time it changes.
console.log(rect);
});
但这只是在它发生变化时才会发生!
如果我们在订阅后想要获得最新值,而不是等待矩形再次更改,该怎么办?嗯,这就是BehaviorSubject
进来的地方。
var rects = new Rx.BehaviorSubject(new Rectangle(-1, -1, 1, 1));
rects.subscribe(function(rect) {
// this would happen twice in this example.
// Once for the initial value (above), and once when it is changed later (below).
console.log(rect);
});
rects.onNext(new Rectangle(-1, -1, 1, 1));
但那不是最初的观察,而且功能不是很好......
以下是如何使用一些内置功能将原始observable做同样的事情,将Observable更改为像BehaviorSubject这样的行为......
var rectsAndDefault = rects.startWith(new Rectangle()); // just give it an initial value
rectsAndDefault.subscribe(function(rect) {
console.log(rect); // happens once for the "startWith" rectangle, and then again for all subsequent changes
})
同样,FRP也不同。它有很大的不同,但这是一项值得学习的大事。你会知道,当它开始让你大开眼界时,你已经开始接受了它。
答案 1 :(得分:1)
答案 2 :(得分:0)
使用Lambdas和Actions作为事件处理程序,您可以在没有指针的情况下离开。
int Offset = 16;
int Size = Offset * 2;
Action<MouseEventArgs> MouseEventArgsHandler = (args) => DrawRectangle(args.x - Offset, args.y - Offset, Size, Size);