首页 > 编程语言 >【玩转 JS 函数式编程_010】3.2 JS 函数式编程筑基之:以函数式编程的方式活用函数(上)

【玩转 JS 函数式编程_010】3.2 JS 函数式编程筑基之:以函数式编程的方式活用函数(上)

时间:2024-10-10 18:20:00浏览次数:9  
标签:sort 函数 palabras 编程 JS 排序 data

写在前面
按照惯例,过长的篇幅分开介绍,本篇为 JavaScript 函数式编程核心基础的第二部分——以函数式编程的方式活用函数的上篇,分别介绍了 JS 函数在排序、回调、Promise 期约、以及连续传递等应用场景下的用法演示。和之前章节相比难度又有一定的提升。准备好了吗?

3.2. 以函数式编程的方式使用函数 Using functions in FP ways

有几种常见的编码模式实际上采用了函数式编程的风格,而您甚至都未曾察觉。本节将逐一考察这些模式的具体表现形式,以便您更快地习惯这种编码风格。这些模式包括:

  • 注入模式:用于筛选不同策略及其他用途;
  • 回调与期约模式:将引入连续传值的相关概念;
  • 填充与插入模式;
  • 立即调用策略模式。

3.2.1. 注入——整理出来 Injection – sorting it out

Array.prototype.sort() 方法提供了第一个将函数作为参数传递的示例。 给定一个待排序的字符串数组,则可以调用 array.sort() 方法。例如,将彩虹的颜色按字母顺序排序,代码如下:

var colors = [
  "violet",
  "indigo",
  "blue",
  "green",
  "yellow",
  "orange",
  "red"
];
colors.sort();
console.log(colors);
// ["blue", "green", "indigo", "orange", "red", "violet", "yellow"]

注意,这里的 sort() 方法并不需要任何参数,数组也能完成排序。默认情况下,此方法按字符串的 ASCII 编码进行排序。因此,如果用它对数字型数组排序则会出错,按这种方式得出的结果,数字 20 将位于 1003 之间,因为 10020 之前(排序元素均被视作字符串)而 20 又在 3 之前:

var someNumbers = [3, 20, 100];
someNumbers.sort();

console.log(someNumbers);
// [100, 20, 3]

假如不考虑数字,只对字符串按默认规则排序。此时如果要对一组西班牙语单词(palabras)进行排序,在遵循恰当的本地化语言环境规则时又会如何呢?可以看到,结果并不正确:

var palabras = ["ñandú", "oasis", "mano", "natural", "mítico", "musical"];
palabras.sort();

console.log(palabras);
// ["mano", "musical", "mítico", "natural", "oasis", "ñandú"] -- wrong result!

拓展知识

对于语言或生物学爱好者而言,ñandú 的英文是 rhea,它一种类似于鸵鸟的飞禽。虽然以 ñ 开头的西班牙语单词并不多,而笔者的国家乌拉圭恰好就有这些鸟类——这就是存在特殊单词的客观原因。

哎呀!在西班牙语中,ñ 介于 no 之间,但 ñandú 排到了末尾。此外,mítico(对应英文 mythical,注意重音字母 í)本应出现在 manomusical 之间,波浪号应该被忽略。要解决这个问题,需要向 sort() 传入正确的比较函数。本例可以使用 localeCompare() 方法:

palabras.sort((a, b) => a.localeCompare(b, "es"));

console.log(palabras);
// ["mano", "mítico", "musical", "natural", "ñandú", "oasis"]

这里的语句 a.localeCompare(b,"es") 会对 ab 进行比较:根据西班牙语("es")的排序规则,当 a 先于 b 时返回一个负值;a 落后于 b 时返回一个正值;两者相等时返回 0

现在排序结果正确了!此时可引入一个新函数 spanishComparison() 来替换所需的字符串比较规则,可使代码更加清晰:

const spanishComparison = (a, b) => a.localeCompare(b, "es");

palabras.sort(spanishComparison);
// sorts the palabras array according to Spanish rules:
// ["mano", "mítico", "musical", "natural", "ñandú", "oasis"]

在接下来的章节中,我们将讨论函数式编程如何让您以更贴近声明式的方式来编写代码,生成更易于理解的代码。这类微小的改变是很有帮助的:当阅读代码的人读到排序这部分时,他们就可以在不借助注释的情况下立即推断出将会执行的逻辑。

小贴士

这种通过注入不同的比较函数来改变 sort() 函数工作方式的模式,实际上是策略设计模式的一种表现形式。第 11 章《实现函数式的设计模式》会具体论述。

提供一个排序函数作为参数(典型的函数式编程风格)还有助于解决其他问题,例如:

  • sort() 仅适用于字符串。要对数字进行排序,必须提供一个数字排序函数,如:myNumbers.sort((a,b) => a-b)
  • 如要按给定属性对对象排序,则需要传入一个与该属性值进行比较的函数。如:myPeople.sort((a,b) => a.age - b.age) 可以按年龄升序对人员进行排序。

小贴士

更多 localeCompare() 介绍,请参阅 MDN 官方文档。您可以指定区域规则、大小写字母的排序规则以及是否忽略标点符号等。但请注意:并非所有浏览器都支持所需的额外参数。

这是一个您以前可能用过的简单示例,但它毕竟是一种函数式编程模式。接下来让我们来看看调用 Ajax 时将函数作为参数的更常见用法。

3.2.2. 回调、期约及延续 Callbacks, promises, and continuations

作一等对象传参的函数最常用的示例应该就是回调(callbacks)和期约(promises)了。在 Node 环境下,读取文件是异步完成的:

const fs = require("fs");

fs.readFile("someFile.txt", (err, data) => {
    if (err) {
        console.error(err); // or throw an error, or otherwise handle the problem
    } else {
        console.log(data.toString()); // do something with the data
    }
});

readFile() 需要一个回调函数——本例为一个匿名函数——它将在文件读取操作完成时被调用。

更好的方法是使用 Promise,详细介绍参考 MDN 文档。有了 Promise,当使用更现代的 fetch() 函数执行 Ajax 调用 Web 服务时,可以按以下代码执行一些逻辑:

fetch("some/remote/url")
  .then(data => {
    // Do some work with the returned data
  })
  .catch(error => {
    // Process all errors here
  });

提示

请注意,如果定义了适当的 processData(data)processError(error) 函数,则代码可以像之前提过的那样,精简为 fetch("some/remote/url").then(processData).catch(processError)

最后,还应该考虑使用 async / await,具体用法详见 MDN 文档 async_functionawait operator

3.2.3. 连续传递风格 Continuation passing style

前面的代码,在调用一个函数的同时,还传递了另一个在 I/O 操作完成时要执行的函数,可以认为是 连续传递风格CPS,Continuation Passing Style)的一种具体体现。这是一种什么样的编码技术呢?不妨从一个实际问题切入理解:如果禁止使用 return 语句,该怎样编程?

乍一看,这个问题似乎无从下手。然而,通过 将回调传函数递给被调用函数,我们便能寻得解决之道:当该过程准备返回控制权给调用者时,它不会实际返回,而是去调用所传递的回调函数。这么一来,回调函数就为被调用函数提供了延续该操作过程的一种途径,CPS 风格中的“连续”(continuation)就是这么来的。CPS 风格本节不具体展开,留待第九章《函数设计——递归》再进行深入研究。值得一提的是,正如我们将看到的那样,CPS 风格将有助于规避递归中的一个重要限制。

弄清“连续”的具体用法,有时是一件颇具挑战的事,但总归是能够达成的。这种编码方式一个有趣的好处在于,通过指定程序的接续方式,可以打破所有常见的程序控制结构(ifwhilereturn 等等),实现想要的任何控制流程。对于处理过程未必是线性的某些问题而言,这类编码风格将会非常有用。当然,这也可能导致您新发明的某种控制结构,远比想象中使用 GOTO 语句的后果更为糟糕!这种做法的危险如下图所示:

图 3.1 弄乱程序流程,可能会发生什么更糟糕的情况呢?

【图 3.1 弄乱程序流程,可能会发生什么更糟糕的情况呢?】

拓展

这部 XKCD 漫画可以在 这里 在线访问。

此外,可供传递的“连续”体也可以不止一个。就像 Promise 那样,可以提供两个或多个回调逻辑参与传递。顺便说一句,这一特性可用于异常处理领域:如果只是允许一个函数可以抛出一个错误,那么该错误就很可能潜在地返回给调用者,而事实上我们并不希望这样。解决问题的关键在于:提供另一个专门处理报错的回调函数(即不同的连续体),以便在抛出异常时使用(第十二章《构建更好的容器——函数式数据类型》将提出一个基于 monads 的新解决方案):

function doSomething(a, b, c, normalContinuation, errorContinuation) {
  let r = 0;
  // ... do some calculations involving a, b, and c,
  // and store the result in r
    
  // if an error happens, invoke:
  // errorContinuation("description of the error")
    
  // otherwise, invoke:
  // normalContinuation(r)
}

利用 CPS 甚至可以超越 JavaScript 现有的控制结构,但这超出了本书的讨论范围,感兴趣的读者可自行研究。

标签:sort,函数,palabras,编程,JS,排序,data
From: https://blog.csdn.net/frgod/article/details/142798845

相关文章

  • 简明逻辑回归中的Sigmoid函数
    Sigmoid函数简介Sigmoid函数是一种常用的激活函数,其定义为: ​这个函数将任意实数值映射到0和1之间,因此特别适合用于二分类问题,输出可以被解释为概率。Sigmoid函数的特性范围:Sigmoid函数的输出值在(0,1)之间。单调性:随着输入 x的增加,输出值单调递增。中心对称:在 x=0......
  • 实验1 现代C++编程初体验
    任务1://现代C++标准库、算法库体验//本例用到以下内容://1.字符串string,动态数组容器类vector、迭代器//2.算法库:反转元素次序、旋转元素//3.函数模板、const引用作为形参#include<iostream>#include<string>#include<vector>#include<algorithm>usin......
  • ctype.h系列的字符函数
    C中有一系列专门处理字符的函数,ctype.h头文件中包含了这些函数的原型。这些函数接收一个字符作为参数,如果该字符属于某特殊的类别,就返回一个非0值(真),反之,返回一个0(假)。                                        ......
  • 简单理解pandas的groupby函数
    pandas中的groupby函数,可以对数据进行分组,然后对分组后的数据进行聚合操作,比如求和、平均值、最大值、最小值等。平时使用groupby处理后,通常使用aggregate函数进行聚合操作。其实,groupby分组之后的聚合操作方法还很多,具体可以参考官方文档。1.读取数据dataframe=pd.DataFrame({......
  • 基于nodejs+vue疫情防控管理系统[开题+源码+程序+论文]计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景近年来,全球范围内频繁爆发的疫情给社会带来了前所未有的挑战,特别是在校园这一人员密集且流动性大的环境中,疫情防控工作显得尤为重要。随着信息技术的飞速发......
  • 基于nodejs+vue疫情防控管理系统[开题+源码+程序+论文]计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景在新冠疫情全球蔓延的背景下,校园作为人员密集、流动性大的特殊场所,疫情防控工作显得尤为重要。随着疫情形势的不断变化,传统的人工管理模式已难以满足高效、......
  • 基于nodejs+vue疫情防控管理系统[开题+源码+程序+论文]计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景近年来,全球范围内频繁爆发的疫情给社会生活和经济发展带来了巨大挑战。特别是在校园环境中,人员密集、流动性大,疫情防控难度尤为突出。为了确保师生的健康安......
  • 《C++与简单人工智能算法:开启智能编程之旅》
    一、引言在当今科技飞速发展的时代,人工智能已经成为了一个热门话题。从智能手机的语音助手到自动驾驶汽车,人工智能技术正在逐渐改变我们的生活。而C++作为一种高效、强大的编程语言,也可以用来实现简单的人工智能算法。本文将带你探索在C++中如何实现一个简单的人工智能算......
  • 实验2_C语言分支与循环基础应用编程
    task1#include<stdio.h>#include<stdlib.h>#include<time.h>#defineN5#defineN1397#defineN2476#defineN321//随机摇学号intmain(){intcnt;intrandom_major,random_no;srand(time(NULL));//以当前系统时间作为随机种子......
  • IEEE全球极限编程大赛10.0题目题解:给出数字N,A,B,求出A,B之间与N互质的数的和(数据范围大)
    题目题目来源第10届IEEE极限编程大赛https://www.hackerrank.com/contests/ieeextreme-challenges/challenges/inti-setsInordertomotivatehisPeruvianstudents,ateacherincludeswordsintheQuechualanguageinhismathclass.Today,hedefinedacurious......