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

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

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


当前回答

免责声明:我的答案是在rx.js的上下文中给出的——一个用于Javascript的“响应式编程”库。

在函数式编程中,不是遍历集合的每个项,而是对集合本身应用高阶函数(hof)。因此,FRP背后的思想是,与其处理每个单独的事件,不如创建一个事件流(使用可观察对象*实现),并对其应用HoFs。通过这种方式,您可以将系统可视化为连接发布者和订阅者的数据管道。

The major advantages of using an observable are: i) it abstracts away state from your code, e.g., if you want the event handler to get fired only for every 'n'th event, or stop firing after the first 'n' events, or start firing only after the first 'n' events, you can just use the HoFs (filter, takeUntil, skip respectively) instead of setting, updating and checking counters. ii) it improves code locality - if you have 5 different event handlers changing the state of a component, you can merge their observables and define a single event handler on the merged observable instead, effectively combining 5 event handlers into 1. This makes it very easy to reason about what events in your entire system can affect a component, since it's all present in a single handler.

可观察对象是可迭代对象的对偶。

Iterable是一个惰性消费序列——迭代器在需要使用每个项时都会拉出它,因此枚举是由消费者驱动的。

可观察对象是一个惰性生成的序列——每一项在被添加到序列时都被推送给观察者,因此枚举是由生产者驱动的。

其他回答

免责声明:我的答案是在rx.js的上下文中给出的——一个用于Javascript的“响应式编程”库。

在函数式编程中,不是遍历集合的每个项,而是对集合本身应用高阶函数(hof)。因此,FRP背后的思想是,与其处理每个单独的事件,不如创建一个事件流(使用可观察对象*实现),并对其应用HoFs。通过这种方式,您可以将系统可视化为连接发布者和订阅者的数据管道。

The major advantages of using an observable are: i) it abstracts away state from your code, e.g., if you want the event handler to get fired only for every 'n'th event, or stop firing after the first 'n' events, or start firing only after the first 'n' events, you can just use the HoFs (filter, takeUntil, skip respectively) instead of setting, updating and checking counters. ii) it improves code locality - if you have 5 different event handlers changing the state of a component, you can merge their observables and define a single event handler on the merged observable instead, effectively combining 5 event handlers into 1. This makes it very easy to reason about what events in your entire system can affect a component, since it's all present in a single handler.

可观察对象是可迭代对象的对偶。

Iterable是一个惰性消费序列——迭代器在需要使用每个项时都会拉出它,因此枚举是由消费者驱动的。

可观察对象是一个惰性生成的序列——每一项在被添加到序列时都被推送给观察者,因此枚举是由生产者驱动的。

好的,从背景知识和阅读你所指向的维基百科页面来看,响应式编程似乎有点像数据流计算,但有特定的外部“刺激”触发一组节点来触发并执行它们的计算。

这非常适合UI设计,例如,触摸用户界面控件(例如,音乐播放应用程序上的音量控制)可能需要更新各种显示项和音频输出的实际音量。当您修改体积(比如一个滑块)时,这将对应于修改有向图中与节点相关的值。

具有“体积值”节点边缘的各种节点将自动被触发,任何必要的计算和更新将自然地贯穿整个应用程序。应用程序对用户刺激“做出反应”。函数式响应式编程只是在函数式语言中实现这一思想,或者通常在函数式编程范式中实现。

有关“数据流计算”的更多信息,请在维基百科或使用您喜欢的搜索引擎上搜索这两个词。总体思想是这样的:程序是一个节点的有向图,每个节点执行一些简单的计算。这些节点通过图链接相互连接,图链接将一些节点的输出提供给其他节点的输入。

当节点触发或执行其计算时,连接到其输出的节点将“触发”或“标记”相应的输入。任何触发/标记/可用所有输入的节点都会自动触发。图可以是隐式的,也可以是显式的,具体取决于响应式编程是如何实现的。

Nodes can be looked at as firing in parallel, but often they are executed serially or with limited parallelism (for example, there may be a few threads executing them). A famous example was the Manchester Dataflow Machine, which (IIRC) used a tagged data architecture to schedule execution of nodes in the graph through one or more execution units. Dataflow computing is fairly well suited to situations in which triggering computations asynchronously giving rise to cascades of computations works better than trying to have execution be governed by a clock (or clocks).

响应式编程引入了这种“执行级联”的思想,似乎以一种类似数据流的方式来考虑程序,但有一个附带条件,即一些节点与“外部世界”挂钩,当这些类似感知的节点发生变化时,执行级联就会被触发。程序的执行看起来就像一个复杂的反射弧。程序在两个刺激之间可能是基本固定的,也可能不是,也可能在两个刺激之间稳定在基本固定的状态。

"non-reactive" programming would be programming with a very different view of the flow of execution and relationship to external inputs. It's likely to be somewhat subjective, since people will likely be tempted to say anything that responds to external inputs "reacts" to them. But looking at the spirit of the thing, a program that polls an event queue at a fixed interval and dispatches any events found to functions (or threads) is less reactive (because it only attends to user input at a fixed interval). Again, it's the spirit of the thing here: one can imagine putting a polling implementation with a fast polling interval into a system at a very low level and program in a reactive fashion on top of it.

伙计,这主意太棒了!为什么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()

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

我在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

看看Rx, net的响应式扩展。他们指出,使用IEnumerable,你基本上是从流中“拉”出来的。IQueryable/IEnumerable上的Linq查询是集合操作,从集合中“吸”出结果。但是在IObservable上使用相同的操作符,你可以编写“反应”的Linq查询。

例如,您可以编写这样的Linq查询 (from MyObservableSetOfMouseMovements中的m m.X<100 m.Y<100 选择新的点(m.X,m.Y))。

有了Rx扩展,就是这样:你有UI代码,它会对传入的鼠标移动流做出反应,并在你处于100,100框时进行绘制……