首页 > 编程语言 >去往js函数式编程(3)

去往js函数式编程(3)

时间:2023-05-30 17:14:06浏览次数:45  
标签:arr const 函数 sum 编程 reduce js 去往 数组

  我们将使用接下来的函数实现:让你更具有声明性,你会发现自己的关注点将转移到你需要什么,而不是如何做;繁琐的细节被隐藏在我们的函数内部。我们将不再编写一系列可能嵌套的 for 循环,而是专注于使用函数作为构建快来制定我们期望的结果。

  使用 reduce()操作可以得到单个值;使用 map()可以得到一个新数组;使用 forEach()可以得到几乎任何类型的结果。如果你在谷歌上搜索,你会找到一些文章声称这些函数不高效,因为手动编写的循环可能更快。虽然这可能是真的,但实际上并不重要。除非你的代码真的受到速度问题的困扰,并且你能够测量到慢速是由于使用这些高阶函数导致的,否则试图避免使用它们并用更长的代码来替代,这样可能会增加错误的概率,实际上并没有太多意义。

  如果你需要多少次遍历数组,执行某个操作,以产生一个单一的值作为结果。这种操作通常可以通过应用 reduce()和 reduceRight()函数来以函数式的方式实现。在通常的函数式编程术语中,我们称之为折叠操作:reduce()被称为 foldl(表示从左到右折叠)或者简称为 fold,而 reduceRight()相应地被称为 foldr。在范畴论的术语中,这两个操作都是折叠映射:将容器中的所有值缩减为单一结果。尝试使用 reduce()或 reduceRight()有几个优点:所有循环控制访问都会自动处理,因此你不可能出现比如“少一次循环”的错误。初始化和处理结果值也是隐式完成的。

  基本上,为了对数组进行 reduce 操作,我们需要提供一个双参数函数,以及一个初始值。

const myArray = [22, 9, 60, 12, 4, 56]
const sum = (x, y) => x + y
const mySum = myArray.reduce(sum, 0) //163

  实际上,你不需要定义 sum 变量,可以直接写 myArray.reduce((x,y)=>x+y,0),然而,以这种方式编写代码的意义更清晰。与编写循环,初始化变量来保存计算结果以及遍历数组进行求和相比,你只需要声明应该执行的操作。使用这些函数进行编程能让你更声明性的方式工作,专注于什么而不是如何。你甚至可以在不提供初始值的情况下执行这个操作:如果你跳过初始值,将使用数组的第一个值,并且内部循环将从数组的第二个元素开始;但是,如果数组为空,而且你没有提供初始值,要小心,因为这将导致运行时错误!

  我们再深入一点,如何计算一组数字的平均值?如果你向某人解释这个问题,你的回答肯定会类似于对列表中所有元素求和,然后除以元素的个数。从编程的角度来看,这不是一个过程性的描述(你没有解释如何求和元素,如何遍历数组),而是一个声明性的描述,因为你说的是要做什么,而不是如何做。

const average = (arr) => arr.reduce(sum, 0) / arr.length

const average2 = (sum, val, ind, arr) => {
  sum += val
  return ind == arr.length - 1 ? sum / arr.length : sum
}

  通过当前的索引,我们可以进行一些花招:在这种情况下,我们始终对值进行求和,但如果我们在数组的末尾,我们还会进行除法运算,以返回数组的平均值。但从可读性的角度来看,第一个更加声明性,更接近数学定义,而不是第二个版本。

  一次计算多个值应该怎么做呢?那可以使用 reduce 每次返回一个对象。

const average3 = (arr) => {
  const sumCount = arr.reduce(
    (accum, value) => ({
      sum: value + accum.sum,
      count: accum.count + 1
    }),
    { sum: 0, count: 0 }
  )

  return sumCount.sum / sumCount.count
}

  只不过,这些来计算平均值的方法越来越晦涩,还是看 reduceRight 吧,和 reduce()的工作方式完全相同,只是从数组的末尾开始循环到数组的开头。对于很多操作,着没有任何区别,但在某些情况下会有所不同。如果我们想要实现一个将字符串反转的函数。一种解决方案是使用 split()将字符串转换为数组,然后反转该数组,最后使用 join()将其重新组合起来。但我们换一种方式,尝试使用 reduceRight():

const reverseString2 = (str) => str.spllit('').reduceRight((x, y) => x + y, '')

  你也许认为先对数组应用 reverse(),然后再使用 reduce(),那么效果将与直接将 reduceRight()应用于原始数组相同。只是,reverse()会改变给定的数组,因此通过反转原始数组,会导致一个意外的副作用!

  使用 map()相比直接使用循环的优势:首先,你不需要编写任何循环,这减少了可能出现错误的可能性。其次,即使原始数组和索引位置存在供你使用,你甚至不需要访问它们,除非你真的需要。最后,会生成一个新数组,因此你的代码是存粹的。在使用 map()时只有两个注意事项:始终从函数中返回值。如果忘记返回值,那么将生成一个填满 undefined 的数组,因为 js 对所有函数都提供默认的返回值 undefined.如果输入数组的元素是对象或数组,并且你在输出数组中包含它们,那么 js 仍然允许访问原始元素。

  我们写一个有用的辅助函数,它在很多场景下都非常方便。一个 range(start,stop)函数,它生成一个由数字组成的数组,数值范围从 start(包含)到 stop(不包含):

const range = (start, stop) =>
  new Array(stop - start).fill(0).map((v, i) => start + i)

let from2To6 = range(2, 7) // [2,3,4,5,6]

  为什么要使用 fill(0),因为 map()会跳过所有未定义的数组元素。lodash 库提供了 range 函数的更强大版本,可以按升序或降序排列,并且还可以指定步长。

const factorialByRange = (n) => range(1, n + 1).reduce((x, y) => x * y, 1)

factorialByRange(5) //120

// 使用range生成A到Z的字母数组
const ALPHABET = range('A'.charCodeAt(), 'Z'.charCodeAt() + 1).map((x) =>
  String.fromCharCode(x)
)

// ["A"..."Z"]

  我们来看下如何通过 reduce 来模拟 flat()和 flatMap(),以便更好地练习。如果一个元素恰好是一个数组,我们就递归地展开它:

const flatAll = (arr) =>
  arr.reduce((f, v) => f.concat(Array.isArray(v) ? flatAll(v) : v), [])

// 首先写一个只展开数组的单个级别的flatOne()函数。

const flatOne1 = (arr) => [].concat(...arr)

const flatOne2 = (arr) => arr.reduce((f, v) => f.concat(v), [])

const flat1 = (arr, n = 1) => {
  if (n === Infinity) {
    return flatAll(arr)
  } else {
    let result = arr
    range(0, n).forEach(() => {
      result = flatOne(result)
    })
    return result
  }
}

const flat2 = (arr, n = 1) =>
  n === Infinity
    ? flatAll(arr)
    : n === 1
    ? flatOne(arr)
    : flat2(flat(flatOne(arr), n - 1))

// 使用reduce()模拟filter()

const myFilter = (arr, fn) =>
  arr.reduce((x, y) => (fn(y) ? x.concat(y) : x), [])

标签:arr,const,函数,sum,编程,reduce,js,去往,数组
From: https://www.cnblogs.com/wlxll/p/17443733.html

相关文章

  • js 轮播图中点击小圆圈图片跳到指定图片
    html代码(部分)<divclass="w"><divclass="main"><!--焦点图模块--><divclass="focusfl"><ahref="javascript:;"class="arrow-l"><</a>......
  • js 获取 image 原始高度
    新版浏览器//这个api仅支持新版本浏览器,旧版还是得创建一个内部图片setTimeout(()=>{letimgRef=this.$refs.imgthis.imgWidth=imgRef.naturalWidththis.imgHeight=imgRef.naturalHeight},10)旧版浏览器(兼容)fu......
  • js 复习
    所有的数组方法concat连接两个或更多的数组,并返回结果copyWithin从数组指定位置拷贝到数组的另一个指定位置中entries返回数组的可迭代对象every检测数值元素,判断是否每个元素都符合条件fill使用一个固定值来填充数组filter过滤find返回符合条件的的数组元素findi......
  • 在node项目中使用log4.js记录日志
    1.在项目根目录创建保存日志文件的文件夹logs2.修改.gitignore文件,添加logs文件夹,这样使用git提交进忽略logs文件夹。node_modules.envlogs3.在config文件夹下新增log4j.js文件保存log4js的配置,路径:./src/config/log4j.js//config.jsletpath=require('pat......
  • Python excejs 执行js文件的时候 报编码错误的问题
    问题执行js的时候报图中的编码错误,直接执行js文件时能正常编译,在网上未找到关于这个问题的文章头疼了好久最终在各位大佬的帮助下解决了问题,便记录了下来:解决办法:一、修改报错文件subprocess.py中的encoding编码:encoding=None--->encoding='utf-8'二、在引包的时......
  • JSON-RPC示例代码(Java实现)
    以下是一个使用Java实现的JSON-RPC示例代码。该示例使用了JSON-RPC2.0规范和Jackson库进行序列化和反序列化。在这个示例中,我们将创建一个服务器和一个客户端,演示如何进行远程过程调用。首先,确保您已经安装了Java开发环境(JDK)和Maven构建工具。接下来,我们将创建一个Maven项目,并......
  • 2023-05-30 浅试nodejs实现登录接口业务(未完,待测试)
    constexpress=require('express');constbodyParser=require('body-parser');constmysql=require('mysql');//创建MySQL连接池constpool=mysql.createPool({host:'localhost',user:'root',password......
  • java编程基础之抽象类
    抽象类观察以下代码有什么问题:Peoplepeople=newpeople("");people.print();结论:实例化people没有意义派派也是一个人,张三、李四等等也是一个人,但是我们在生活当中呢,人是一种分类,并不是单个个体,他是我们抽象出来的,不具体,所以说他没有现实世界当中对应一个人这样的一个人存在,这......
  • Python 读取图片 转 base64 并生成 JSON
    Python读取图片转base64并生成JSONimportjsonimportbase64img_path=r'D:\OpenSource\PaddlePaddle\PaddleOCR\images\005.jpeg';withopen(img_path,'rb')asfile:image_data1=file.read()image=base64.b64encode(image_data1).de......
  • 【Python】将中文字符写入json文件
    ensure_asciiimportjsondict1={'name':'时间','data':['2023-04-1305:00']},{'name':'雨量mm/h','data':['0.0000']},{'name':'温度℃','data':[&......