缩小、折叠或扫描(左/右)?

2023-12-06

我应该什么时候使用reduceLeft, reduceRight, foldLeft, foldRight, scanLeft or scanRight?

我想要对它们的差异有一个直观/概述 - 可能有一些简单的例子。


一般来说,所有 6 个折叠函数都对集合的每个元素应用二元运算符。每个步骤的结果都会传递到下一步(作为二元运算符的两个参数之一的输入)。这样我们就可以cumulate一个结果。

reduceLeft and reduceRight累积单个结果。

foldLeft and foldRight使用起始值累积单个结果。

scanLeft and scanRight使用起始值累积中间累积结果的集合。

积累

从左往前...

带有元素的集合abc和一个二元运算符add我们可以探索从集合的 LEFT 元素(从 A 到 C)前进时不同折叠函数的作用:

val abc = List("A", "B", "C")

def add(res: String, x: String) = { 
  println(s"op: $res + $x = ${res + x}")
  res + x
}

abc.reduceLeft(add)
// op: A + B = AB
// op: AB + C = ABC    // accumulates value AB in *first* operator arg `res`
// res: String = ABC

abc.foldLeft("z")(add) // with start value "z"
// op: z + A = zA      // initial extra operation
// op: zA + B = zAB
// op: zAB + C = zABC
// res: String = zABC

abc.scanLeft("z")(add)
// op: z + A = zA      // same operations as foldLeft above...
// op: zA + B = zAB
// op: zAB + C = zABC
// res: List[String] = List(z, zA, zAB, zABC) // maps intermediate results


从右和向后...

如果我们从 RIGHT 元素开始并向后移动(从 C 到 A),我们会注意到现在second二元运算符的参数累加结果(运算符是相同的,我们只是交换了参数名称以明确它们的角色):

def add(x: String, res: String) = {
  println(s"op: $x + $res = ${x + res}")
  x + res
}

abc.reduceRight(add)
// op: B + C = BC
// op: A + BC = ABC  // accumulates value BC in *second* operator arg `res`
// res: String = ABC

abc.foldRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: String = ABCz

abc.scanRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: List[String] = List(ABCz, BCz, Cz, z)

.

去累积

从左往前...

如果我们要去累积从集合的左侧元素开始减去一些结果,我们将通过第一个参数累积结果res我们的二元运算符minus:

val xs = List(1, 2, 3, 4)

def minus(res: Int, x: Int) = {
  println(s"op: $res - $x = ${res - x}")
  res - x
}

xs.reduceLeft(minus)
// op: 1 - 2 = -1
// op: -1 - 3 = -4  // de-cumulates value -1 in *first* operator arg `res`
// op: -4 - 4 = -8
// res: Int = -8

xs.foldLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: Int = -10

xs.scanLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: List[Int] = List(0, -1, -3, -6, -10)


从右和向后...

但现在请留意 xRight 的变体!请记住,xRight 变量中的(去)累积值被传递给second范围res我们的二元运算符minus:

def minus(x: Int, res: Int) = {
  println(s"op: $x - $res = ${x - res}")
  x - res
}

xs.reduceRight(minus)
// op: 3 - 4 = -1
// op: 2 - -1 = 3  // de-cumulates value -1 in *second* operator arg `res`
// op: 1 - 3 = -2
// res: Int = -2

xs.foldRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: Int = -2

xs.scanRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: List[Int] = List(-2, 3, -1, 4, 0) 

最后一个 List(-2, 3, -1, 4, 0) 可能不是您直观所期望的!

如您所见,您可以通过简单地运行 scanX 来检查 FoldX 正在做什么,并调试每个步骤的累积结果。

底线

  • 累积结果reduceLeft or reduceRight.
  • 累积结果foldLeft or foldRight如果你有一个起始值。
  • 累积中间结果的集合scanLeft or scanRight.

  • 如果您想走,请使用 xLeft 变体forwards通过收集。

  • 如果您想继续,请使用 xRight 变体向后通过收集。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

缩小、折叠或扫描(左/右)? 的相关文章

随机推荐

  • 管理多个版本的 JDK:无效源发布错误

    我安装了jdk 1 6和1 7 我已将 JAVA HOME 变量更新为 C Program Files Java jdk1 6 0 24 C Program Files Java jdk1 7 0 但是当我尝试运行java代码时 gt ja
  • 如何在android中的AsyncTask中使用进度对话框

    我正在开发一个需要用户登录的应用程序 通过网址登录可以正常工作 但是 当我添加 AsyncTask 来添加进度对话框时 该应用程序不会更改意图 我已经尝试了我所知道的所有可能的方法 但徒劳无功 下面是代码 package com epoli
  • ASP.NET 2.0 到 4.0

    我有一个 ASP NET 2 0 网站 我打算升级到4 0 有什么特别需要注意的事情吗 或者我只是选择 4 0 框架并重新编译 VS 2010 将更新 web config 以反映更改 有一些事情您必须注意 阅读所有更改并在测试时将其放在您
  • 如何在 Visual Studio 2008 中为自定义用户控件提供设计时支持?

    我正在为 NET Compact Framework 创建自定义用户控件 并且希望保留设计时支持 我想在 Visual Studio 2008 中 这不再是以前那样疯狂 几乎不可能完成的任务 我希望有一个简单的教程可以告诉我 这很简单 任何
  • 使用 XStream 将 Java 对象序列化为 XML

    问题是 每次执行 main 方法时 a xml 的旧内容都会丢失并被新内容替换 如何将内容追加到a xml文件中而不丢失之前的信息 import java io FileNotFoundException import java io Pr
  • 使用 Youtube Data API 编辑品牌帐户播放列表

    我有一个 Gmail 帐户 无 google plus 其中包含许多品牌帐户 每个品牌帐户都有自己的 YouTube 频道 发布不同的视频内容 我编写了一个简单的 Google Apps 脚本 用于将视频插入品牌帐户播放列表之一 但这不起作
  • 空手道:多部分文件:读取有效,但传递内容值失败

    我有一个带有多部分请求的端点 该请求将两个文件作为请求参数的一部分 我尝试使用下面的代码片段进行读取并且它有效 但我的用例是从文件中获取内容并将其传递给 value 参数 我可以将内容作为文件传递吗 如果我尝试从目录读取文件 工作代码是 工
  • TeamSpeak:查询已连接客户端数量

    我目前正在使用 TeamSpeak 的 ServerQuery 功能通过 PHP 在我的网站上显示所有频道和连接的用户 现在它看起来像这样 对粗略的用户名 频道标题表示歉意 它的作用是显示频道和用户名 但是 我不希望它这样做 我不想显示已连
  • 在 Windows 上安装 PyGraphViz,Python 2.7 graphviz-2.36

    我已经提到过link 但仍然面临问题 我在 Windows 7 上成功安装了 graphviz 现在我想安装 pygraphviz 我下载了 pygraphviz zip 文件并解压缩 我修改了 setup py 并添加了以下文本 Wind
  • 如何在Python中找到两个日期时间对象之间的时间差?

    如何判断两个时间之间的时差 以分钟为单位 datetime物体 gt gt gt import datetime gt gt gt first time datetime datetime now gt gt gt later time d
  • MSI 安装程序扩展如何找到另一个应用程序的安装目录?

    我有一个主要应用程序TheApp它支持插件 应用程序 A 默认安装在 ProgramFiles TheApp 但黄金所有者希望用户可以自定义它 因此它的位置可能会根据安装时用户的输入而变化 插件通过将其复制到子目录来安装Packages安装
  • 如何编写采用“u32”或“&u32”的任何迭代器的通用函数?

    我正在尝试编写一个处理整数序列的函数 fn process one n u32 fn process
  • 用于验证全名的 Java 正则表达式仅允许空格和字母

    我希望正则表达式仅验证字母和空格 基本上这是为了验证全名 前任 史蒂夫 柯林斯先生或史蒂夫 柯林斯我尝试了这个正则表达式 a zA Z 但没有成功 有人可以帮助我吗 附注我使用Java public static boolean valid
  • 从错误的线程访问领域 - Swift 3

    在我的顶部UITableViewController如下 let queue DispatchQueue label background 删除任务时 将执行以下操作 self queue async autoreleasepool let
  • Java.sql.Date 转 Oracle 数据库日期和时间戳

    我正在使用 Spring JDBC 模板进行 jdbc 操作 由于我使用的是 BeanPropertySqlParameterSource bean 的 START TIME 变量被分配为 java sql date 类型 在Oracle数
  • SQL Server 模式 SQL

    我有一张表格 列出了每个班级的学生成绩 我想要一个如下所示的结果集 BIO B CHEM C 其中 B 和 C 是该类的模式 我可以获得所有成绩的模式 但不确定如何获得每个班级的模式 这里 在 SQL 2005 2008 上是这样的 WIT
  • 用 Swift 重构解决方案

    我一直在通过 HackerRank 测试用例来学习编码考试 大部分情况下我都做得很好 但我对一些简单的案例很感兴趣 当我看不到解决方案时 你们都会帮助我 我正在解决这个问题 https www hackerrank com challeng
  • 如何通过SNMP查找打印机计数器

    我目前正在从事一个项目 涉及通过 SNMP 从打印机获取信息 现在我一直在测试 使用的打印机是 Lexmark X950 我一直在努力解决的一个问题是 我希望这个程序也适用于 HP Kyocera 或 Brother 打印机 但我使用的 O
  • 如何从Flask中的Form中获取多个选定的项目

    随着
  • 缩小、折叠或扫描(左/右)?

    我应该什么时候使用reduceLeft reduceRight foldLeft foldRight scanLeft or scanRight 我想要对它们的差异有一个直观 概述 可能有一些简单的例子 一般来说 所有 6 个折叠函数都对集