我读过维基百科上关于响应式编程的文章。我还读过一篇关于函数式响应式编程的小文章。这些描述相当抽象。

函数式响应式编程(FRP)在实践中意味着什么? 反应式编程(相对于非反应式编程?)由什么组成?

我的背景是命令式/OO语言,所以与此范例相关的解释将受到赞赏。


当前回答

根据前面的答案,在数学上,我们似乎只是以更高的顺序思考。我们不认为值x具有类型x,而是考虑函数x: T→x,其中T是时间的类型,可以是自然数、整数或连续统。当我们用编程语言写y:= x + 1时,我们实际上是指方程y(t) = x(t) + 1。

其他回答

伙计,这主意太棒了!为什么1998年的时候我没有发现?总之,这是我对Fran教程的理解。建议是最受欢迎的,我正在考虑开始一个基于此游戏引擎。

import pygame
from pygame.surface import Surface
from pygame.sprite import Sprite, Group
from pygame.locals import *
from time import time as epoch_delta
from math import sin, pi
from copy import copy

pygame.init()
screen = pygame.display.set_mode((600,400))
pygame.display.set_caption('Functional Reactive System Demo')

class Time:
    def __float__(self):
        return epoch_delta()
time = Time()

class Function:
    def __init__(self, var, func, phase = 0., scale = 1., offset = 0.):
        self.var = var
        self.func = func
        self.phase = phase
        self.scale = scale
        self.offset = offset
    def copy(self):
        return copy(self)
    def __float__(self):
        return self.func(float(self.var) + float(self.phase)) * float(self.scale) + float(self.offset)
    def __int__(self):
        return int(float(self))
    def __add__(self, n):
        result = self.copy()
        result.offset += n
        return result
    def __mul__(self, n):
        result = self.copy()
        result.scale += n
        return result
    def __inv__(self):
        result = self.copy()
        result.scale *= -1.
        return result
    def __abs__(self):
        return Function(self, abs)

def FuncTime(func, phase = 0., scale = 1., offset = 0.):
    global time
    return Function(time, func, phase, scale, offset)

def SinTime(phase = 0., scale = 1., offset = 0.):
    return FuncTime(sin, phase, scale, offset)
sin_time = SinTime()

def CosTime(phase = 0., scale = 1., offset = 0.):
    phase += pi / 2.
    return SinTime(phase, scale, offset)
cos_time = CosTime()

class Circle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius
    @property
    def size(self):
        return [self.radius * 2] * 2
circle = Circle(
        x = cos_time * 200 + 250,
        y = abs(sin_time) * 200 + 50,
        radius = 50)

class CircleView(Sprite):
    def __init__(self, model, color = (255, 0, 0)):
        Sprite.__init__(self)
        self.color = color
        self.model = model
        self.image = Surface([model.radius * 2] * 2).convert_alpha()
        self.rect = self.image.get_rect()
        pygame.draw.ellipse(self.image, self.color, self.rect)
    def update(self):
        self.rect[:] = int(self.model.x), int(self.model.y), self.model.radius * 2, self.model.radius * 2
circle_view = CircleView(circle)

sprites = Group(circle_view)
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            running = False
    screen.fill((0, 0, 0))
    sprites.update()
    sprites.draw(screen)
    pygame.display.flip()
pygame.quit()

简而言之:如果每个组成部分都可以被视为一个数字,那么整个系统就可以被视为一个数学方程,对吗?

FRP是函数式编程(编程范式建立在一切都是函数的思想上)和响应式编程范式(建立在一切都是流的思想上(观察者和可观察的哲学))的结合。它应该是世界上最好的。

看看Andre Staltz关于响应式编程的文章。

我在Clojure reddit上找到了一个关于FRP的视频。即使你不懂Clojure,也很容易理解。

这是视频:http://www.youtube.com/watch?v=nket0K1RXU4

这是视频后半段提到的来源:https://github.com/Cicayda/yolk-examples/blob/master/src/yolk_examples/client/autocomplete.cljs

关于响应式编程的简短而清晰的解释出现在Cyclejs -响应式编程中,它使用了简单和可视化的示例。

一个[模块/组件/对象]是反应性的意味着它是完全负责的 通过对外部事件的反应来管理自己的状态。 这种方法的好处是什么?这就是控制反转, 主要是因为[module/Component/object]对自己负责,使用私有方法来改进封装。

这是一个很好的起点,而不是一个完整的知识来源。从那里你可以跳到更复杂和更深入的文件。

在阅读了许多页关于FRP的文章后,我终于看到了这篇关于FRP的启发性文章,它最终让我明白了FRP的真正含义。

下面我引用海因里希·阿费尔马斯(活性香蕉的作者)的话。

What is the essence of functional reactive programming? A common answer would be that “FRP is all about describing a system in terms of time-varying functions instead of mutable state”, and that would certainly not be wrong. This is the semantic viewpoint. But in my opinion, the deeper, more satisfying answer is given by the following purely syntactic criterion: The essence of functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration. For instance, take the example of a counter: you have two buttons labelled “Up” and “Down” which can be used to increment or decrement the counter. Imperatively, you would first specify an initial value and then change it whenever a button is pressed; something like this: counter := 0 -- initial value on buttonUp = (counter := counter + 1) -- change it later on buttonDown = (counter := counter - 1) The point is that at the time of declaration, only the initial value for the counter is specified; the dynamic behavior of counter is implicit in the rest of the program text. In contrast, functional reactive programming specifies the whole dynamic behavior at the time of declaration, like this: counter :: Behavior Int counter = accumulate ($) 0 (fmap (+1) eventUp `union` fmap (subtract 1) eventDown) Whenever you want to understand the dynamics of counter, you only have to look at its definition. Everything that can happen to it will appear on the right-hand side. This is very much in contrast to the imperative approach where subsequent declarations can change the dynamic behavior of previously declared values.

所以,在我的理解中,FRP程序是一组方程:

J是离散的:1,2,3,4…

F依赖于t所以这包含了外部刺激模型的可能性

程序的所有状态都封装在变量x_i中

FRP库考虑了进度时间,换句话说,从j到j+1。

我会在这个视频中更详细地解释这些方程。

编辑:

在最初的回答大约2年后,最近我得出结论,FRP实现还有另一个重要的方面。它们需要(通常也会)解决一个重要的实际问题:缓存失效。

x_i-s的方程描述了一个依赖关系图。当x_i在j时刻发生变化时,并不需要更新j+1时刻的所有其他x_i'值,因此并不需要重新计算所有依赖项,因为有些x_i'可能与x_i无关。

而且,改变的x_i-s可以被增量更新。例如,让我们考虑Scala中的映射操作f=g.map(_+1),其中f和g是int类型的列表。这里f对应于x_i(t_j) g是x_j(t_j)现在,如果我将一个元素前置到g中,那么对g中的所有元素执行映射操作将是浪费的。一些FRP实现(例如reflect - FRP)旨在解决这个问题。这个问题也称为增量计算。

换句话说,FRP中的行为(x_i-s)可以被认为是缓存的计算。如果某些f_i-s确实发生了变化,FRP引擎的任务就是有效地使这些缓存(x_i-s)失效并重新计算。