在c#中实现反应式编程/函数

Pro*_*gga 7 c# reactive-programming

我最近正在阅读有关游戏引擎设计的内容,并最终绊倒了这个:什么是(功能)反应式编程?

我想知道如何实现第二个最高评级答案中给出的例子.在C++中,很容易将指针传递给存储鼠标坐标的值,只返回值而不是int.好吧,我们不能在C#中真正做到这一点,所以这是我们的第一个问题.我们是否需要调用一些"更新"功能来保持所有值都是最新的?

其次,如何处理语法?分配值是直截了当的.但是,每当我要求它时,做"让鼠标定位并从中获取14分钟"的事情稍微复杂一点......

最后,我想知道如何直接引用C#中的任何对象返回一个值.例如

int test = 1;
Run Code Online (Sandbox Code Playgroud)

测试将返回1.所以我可以做1 + test这样的事情= 2

但如果我有一个实例

public class ReactiveInt {
     int myValue
}
Run Code Online (Sandbox Code Playgroud)

在尝试将int添加到一起时,我不能只做我上面做的事情.

对不起,我想这是一个广泛的问题.如果可以给出一个简单的例子来演示类似于答案中讨论的功能,我想我的所有问题都会得到解答.

cwh*_*ris 8

问题

问题1

在尝试将int添加到一起时,我不能只做我上面做的事情.

嗯,这实际上就是重点.在反应式编程中,您不希望强制性地将两个数字相加,而是希望根据其他数字定义新数字.因此,c = a + b例如c始终等于a + b,即使当ab改变:c在相对于反应性是ab.

var a = new BehaviorSubject(3);
var b = new BehaviorSubject(1);
var c = Rx.Observable.combineLatest(a, b, function(vals) {
    return vals[0] + vals[1];
});
Run Code Online (Sandbox Code Playgroud)

问题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);
    });
Run Code Online (Sandbox Code Playgroud)

简化端口

由于矩形仅根据一个相关值(或事件)定义,因此您可以将其简化为以下内容:

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);
        });
Run Code Online (Sandbox Code Playgroud)

使用它

此时,您可以使用它来组成其他可观察序列(随时间变化的值).

var area = boundingRect.map(function(rect) {
    return rect.getSize();
    });
Run Code Online (Sandbox Code Playgroud)

或直接订阅.

boundingRect.subscribe(function (rect) {
    // perform some action with the rect each time it changes.
    console.log(rect);
    });
Run Code Online (Sandbox Code Playgroud)

但那只是在它改变的时候!

如果我们在订阅时想要获得最新值,而不是等待矩形再次更改,该怎么办?嗯,这就是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));
Run Code Online (Sandbox Code Playgroud)

但这不是原始的可观察性,而且它不是很实用......

以下是如何使用原始的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
    })
Run Code Online (Sandbox Code Playgroud)

再次,FRP是不同的.它有很大的不同,但这是一项值得学习的大事.当你开始思考时,你会知道你已经开始得到它了.


d_s*_*ell 0

使用 Lambda 和 Actions 作为事件处理程序,您无需指针即可摆脱困境。

int Offset = 16;
int Size = Offset * 2;
Action<MouseEventArgs> MouseEventArgsHandler = (args) => DrawRectangle(args.x - Offset, args.y - Offset, Size, Size);
Run Code Online (Sandbox Code Playgroud)