JS函数式编程【译】4.4 函数式响应式编程

函数式响应式编程

我们再来建立另一种类型的应用,他的工作方式差不多,都是用函数式编程来响应状态变化。 但是这回应用不会依赖于事件监听。

来想象一下,你在一个新闻媒体公司工作,你的老板让你建立一个web应用来跟踪竞选日的政府竞选结果。 数据会根据地方选区的结果持续流动,所以显示在页面上的结果是具有响应式特征的。然而我们还需跟踪每一个区域的结果, 所以会有多个对象需要追踪。

我们与其建立一个巨大的面向对象的层次结构来为接口建模,不如把它描述为声明式的不可变数据。 我们可以把它转换为纯函数或者半纯函数,半纯函数在必须保持的状态要更新时才产生副作用(理想状况下不会很多)。

我们将使用Bacon.js库,它可以快速开发函数式响应式编程(FRP)的应用。这个应用只在选举日使用, 我们的老板认为它花费的时间应该与之成比例。通过函数式编程和像Bacon.js这样的库,我们将仅需要一半的时间。

不过首先我们需要一些对象来描述选区,比如州、省、区等等。

function Region(name, percent, parties) {
  // 可变属性:
  this.name = name;
  this.percent = percent; // % of precincts reported
  this.parties = parties; // political parties
  // 返回一段HTML
  this.render = function() {
    var lis = this.parties.map(function(p) {
      return '' + p.name + ': ' + p.votes + '';
    });
    var output = '' + this.name + '';
    output += '' + lis.join('') + '';
    output += 'Percent reported: ' + this.percent;
    return output;
  }
}

function getRegions(data) {
  return JSON.parse(data).map(function(obj) {
    return new Region(obj.name, obj.percent, obj.parties);
  });
}
var url = 'http://api.server.com/election-data?format=json';
var data = jQuery.ajax(url);
var regions = getRegions(data);
app.container.innerHTML = regions.map(function(r) {
  return r.render();
}).join('');

上面的代码可以满足对静态选举结果列表的展示,然而我们需要一种动态更新选区的方式。是时候来点Bacon和FRP了。

响应式编程

bacon有个函数,Bacon.fromPoll(),用于创建事件流,它让事件成为在一定时间间隔被调用的函数。 还有stream.subscribe()函数让我们可以订阅(subsribe)一个针对这个流的处理函数。由于它是惰性的, 如果没有订阅者(subscriber)流实际上就不去做任何事情。

var eventStream = Bacon.fromPoll(10000, function() {
  return Bacon.Next;
});
var subscriber = eventStream.subscribe(function() {
  var url = 'http://api.server.com/election-data?format=json';
  var data = jQuery.ajax(url);
  var newRegions = getRegions(data);
  container.innerHTML = newRegions.map(function(r) {
    return r.render();
  }).join('');
});

大体上就是把它放到一个每10秒运行一次的循环中,我们的工作完成了。但是这个方法将会不断的ping网络, 并且非常低效。这还不是很函数式。让我们继续深入挖掘一下Bacon.js库。

Beacon里有EventStreams和Properties参数。Properties可以想作是“魔术”变量,它随着事件的响应不断变化。 实际上这不是魔术,因为他们依赖于事件流。属性的不断变化与事件流相关。

Bacon.js里还有一个戏法。Bacon.fromPromise()函数是一种利用promise把事件搞成流的方式。 jQuery从1.5.0版本开始实现了promise接口,所以我们所要做的仅仅是写一个AJAX查询函数, 在异步调用完成时触发事件。每当promise被处理时,他就调用EventStream的订阅者(subscribers)。

var url = 'http://api.server.com/election-data?format=json';
var eventStream = Bacon.fromPromise(jQuery.ajax(url));
var subscriber = eventStream.onValue(function(data) {
  newRegions = getRegions(data);
  container.innerHTML = newRegions.map(function(r) {
    return r.render();
  }).join('');
}

promise可以想成是一个初始值;通过Bacon.js,我们可以对初始值惰性地等待。

把它们搁到一块儿

现在我们学完了响应式的内容,最后我们可以用些代码来玩一玩它。

我们可以通过对纯函数的链式调用改变订阅者来做些事情,比如求和或者过滤不想要的结果, 我们只需通过我们所创建的对按钮的onclick()处理函数来完成这些。

// 在函数外创建事件流
var eventStream = Bacon.onPromise(jQuery.ajax(url));
var subscribe = null;
var url = 'http://api.server.com/election-data?format=json';
// 未被改变的订阅者
$('button#showAll').click(function() {
  var subscriber = eventStream.onValue(function(data) {
    var newRegions = getRegions(data).map(function(r) {
      return new Region(r.name, r.percent, r.parties);
    });
    container.innerHTML = newRegions.map(function(r) {
      return r.render();
    }).join('');
  });
});
// 显示全部选举情况的按钮
$('button#showTotal').click(function() {
  var subscriber = eventStream.onValue(function(data) {
    var emptyRegion = new Region('empty', 0, [{
      name: 'Republican',
      votes: 0
    }, {
      name: 'Democrat',
      votes: 0
    }]);
    var totalRegions = getRegions(data).reduce(function(r1, r2) {
      newParties = r1.parties.map(function(x, i) {
        return {
          name: r1.parties[i].name,
          votes: r1.parties[i].votes + r2.parties[i].votes
        };
      });
      newRegion = new Region('Total', (r1.percent + r2.percent) / 2,
        newParties);
      return newRegion;
    }, emptyRegion);
    container.innerHTML = totalRegions.render();
  });
});
// 只显示报告大于50%的选区
$('button#showMostlyReported').click(function() {
  var subscriber = eventStream.onValue(function(data) {
    var newRegions = getRegions(data).map(function(r) {
      if (r.percent > 50) return r;
      else return null;
    }).filter(function(r) {
      return r != null;
    });
    container.innerHTML = newRegions.map(function(r) {
      return r.render();
    }).join('');
  });
});

它的美丽之处在于:当用户点击按钮时,事件流并没有变化,但是订阅者变化了,这就使所有的工作很平顺。

第四章总结

JavaScript是一门美丽的语言。 它内在美确是因函数式编程而闪耀。它赋予了它良好的扩展性。事实上,打开了函数式的闸门使得头等函数可以做如此之多的事情。 概念互相堆叠,越来越高。

在这章,我们深入了函数式编程的范例,包括函数工厂、柯里化、函数组合等等。我们用这些概念建立了非常模块化的应用。 然后我们展示了如何使用一些函数库利用同样的概念,函数组合,来控制执行顺序。

这章覆盖了几个函数式编程的风格:数据泛型编程、基本上函数式的编程以及函数响应式编程。 他们之间并没有很大不同,只是在不同场景应用的不同函数式编程模式而已。

上一章粗略提及了一些叫范畴论的东西,下一章我们将学习关于它的更多内容以及如何使用它。

下一章 范畴轮
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图像识别技术在病虫害检测中的应用是一个快速发展的领域,它结合了计算机视觉和机器学习算法来自动识别和分类植物上的病虫害。以下是这一技术的一些关键步骤和组成部分: 1. **数据收集**:首先需要收集大量的植物图像数据,这些数据包括健康植物的图像以及受不同病虫害影响的植物图像。 2. **图像预处理**:对收集到的图像进行处理,以提高后续分析的准确性。这可能包括调整亮度、对比度、去噪、裁剪、缩放等。 3. **特征提取**:从图像中提取有助于识别病虫害的特征。这些特征可能包括颜色、纹理、形状、边缘等。 4. **模型训练**:使用机器学习算法(如支持向量机、随机森林、卷积神经网络等)来训练模型。训练过程中,算法会学习如何根据提取的特征来识别不同的病虫害。 5. **模型验证和测试**:在独立的测试集上验证模型的性能,以确保其准确性和泛化能力。 6. **部署和应用**:将训练好的模型部署到实际的病虫害检测系统中,可以是移动应用、网页服务或集成到智能农业设备中。 7. **实时监测**:在实际应用中,系统可以实时接收植物图像,并快速给出病虫害的检测结果。 8. **持续学习**:随着时间的推移,系统可以不断学习新的病虫害样本,以提高其识别能力。 9. **用户界面**:为了方便用户使用,通常会有一个用户友好的界面,显示检测结果,并提供进一步的指导或建议。 这项技术的优势在于它可以快速、准确地识别出病虫害,甚至在早期阶段就能发现问题,从而及时采取措施。此外,它还可以减少对化学农药的依赖,支持可持续农业发展。随着技术的不断进步,图像识别在病虫害检测中的应用将越来越广泛。
软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测试面试题软件测
轻型卡车零部件销售平台是一个专门针对轻型卡车用户和维修服务商设计的电子商务系统,旨在提供一个便捷、高效的在线购买渠道,用于获取各种卡车零部件和配件。该平台通过整合供应链、优化库存管理和提供卓越的客户服务,为轻型卡车的维护和修理提供支持。以下是该平台可能包含的一些关键特性: 1. **产品目录**:提供一个全面的在线目录,展示各种轻型卡车的零部件,包括发动机部件、悬挂系统、电子设备、车身零件等。 2. **品牌和型号筛选**:允许用户根据品牌、型号、年份和其他规格筛选零部件,确保用户能够快速找到适配其车辆的配件。 3. **库存管理**:实时更新库存状态,确保用户能够看到最新的产品可用性,并在缺货时提供预计到货时间。 4. **在线订购**:用户可以通过安全的在线支付系统轻松下单购买所需零部件,支持多种支付方。 5. **价格和促销**:提供有竞争力的价格和定期促销活动,包括折扣、优惠券和捆绑销售,以吸引和保留客户。 6. **用户评价和反馈**:允许用户对购买的产品进行评价和提供反馈,帮助其他用户做出购买决策。 7. **快速配送**:与物流服务提供商合作,确保订单能够快速、准确地配送到客户手中。 8. **售后服务**:提供客户支持服务,解答用户关于产品和订单的问题,并处理退换货事宜。 9. **技术支持**:提供在线技术支持,帮助用户解决安装和使用零部件时遇到的技术问题。 10. **移动应用**:开发移动应用程序,让用户能够通过智能手机或平板电脑随时随地访问平台和进行购买。 11. **B2B功能**:为商业客户提供批量购买选项、账户管理和定制报价服务。 12. **数据分析**:利用数据分析工具来优化销售策略,提高用户满意度,并增强用户体验。 轻型卡车零部件销售平台通过提供一站的购物体验,帮助用户节省时间和金钱,同时确保他们能够获得高质量的产品和服务。随着电子商务的不断进步和物流网络的扩展,这样的平台在提供便利性、选择性和可靠性方面发挥着越来越重要的作用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值