我读过维基百科上关于响应式编程的文章。我还读过一篇关于函数式响应式编程的小文章。这些描述相当抽象。
函数式响应式编程(FRP)在实践中意味着什么? 反应式编程(相对于非反应式编程?)由什么组成?
我的背景是命令式/OO语言,所以与此范例相关的解释将受到赞赏。
我读过维基百科上关于响应式编程的文章。我还读过一篇关于函数式响应式编程的小文章。这些描述相当抽象。
函数式响应式编程(FRP)在实践中意味着什么? 反应式编程(相对于非反应式编程?)由什么组成?
我的背景是命令式/OO语言,所以与此范例相关的解释将受到赞赏。
当前回答
关于响应式编程的简短而清晰的解释出现在Cyclejs -响应式编程中,它使用了简单和可视化的示例。
一个[模块/组件/对象]是反应性的意味着它是完全负责的 通过对外部事件的反应来管理自己的状态。 这种方法的好处是什么?这就是控制反转, 主要是因为[module/Component/object]对自己负责,使用私有方法来改进封装。
这是一个很好的起点,而不是一个完整的知识来源。从那里你可以跳到更复杂和更深入的文件。
其他回答
Andre Staltz的这篇文章是迄今为止我所见过的最好、最清楚的解释。
以下是文章中的一些引述:
响应式编程是使用异步数据流进行编程。 最重要的是,你会得到一个神奇的功能工具箱来组合、创建和过滤任何这些流。
下面是文章中精彩图表的一个例子:
Paul Hudak的书,The Haskell School of Expression,不仅是对Haskell的很好的介绍,而且还花了相当多的时间在FRP上。如果你是FRP的初学者,我强烈推荐它让你了解FRP是如何工作的。
还有一本看起来像是这本书(2011年出版,2014年更新)的新重写版——哈斯克尔音乐学院。
伙计,这主意太棒了!为什么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()
简而言之:如果每个组成部分都可以被视为一个数字,那么整个系统就可以被视为一个数学方程,对吗?
免责声明:我的答案是在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是一个惰性消费序列——迭代器在需要使用每个项时都会拉出它,因此枚举是由消费者驱动的。
可观察对象是一个惰性生成的序列——每一项在被添加到序列时都被推送给观察者,因此枚举是由生产者驱动的。
有一种简单的方法可以直观地了解它是什么样子的,那就是把你的程序想象成一个电子表格,所有的变量都是单元格。如果电子表格中的任何单元格发生变化,则引用该单元格的任何单元格也会发生变化。玻璃钢也是一样。现在想象一下,一些单元格会自己改变(或者更确切地说,是从外部世界中获取的):在GUI情况下,鼠标的位置就是一个很好的例子。
这必然会错过很多东西。当你实际使用FRP系统时,这个比喻很快就被打破了。首先,通常也会尝试建模离散事件(例如鼠标被点击)。我把这个放在这里只是为了让你们了解它是什么样的。