本节知识点:

  1. 操作:重复
  2. 操作:为每个项目重复
  3. 操作:拆分文本
  4. 操作:匹配文本
  5. 操作:获取匹配文本的组
  6. 操作:替换文本
  7. 捷径知识点:重复项目
  8. 捷径知识点:重复索引
  9. 正则表达式知识点:多次匹配字母与数字

两种重复

重复是另一个我们在现实生活中经常会做的事,但是我们很少会意识到,重复有两个含义。

它的第一个含义是「循环」。比如刚开始学习日语的时候,一般都要把假名写很多遍,这就是一种重复。

这种重复在捷径中对应的操作是——「重复」:

操作重复

它的关键在于次数。像抄假名的作业一样,循环够足够的次数,它就完成了它的使命。比如说重复 5 遍假名「あ」,使用捷径来完成就是:

重复 5 次「あ」

我们先做一个「文本」操作,里面写上「あ」。再用「重复」包住它,并把数值选择为 5,运行它,即可获得 5 个「あ」。

这种类型的重复,在日常生活中是一种有意识的重复,因为我们是有意识地在重复抄写,我们甚至在默数自己抄了多少遍,还差几个。

除了这种我们有意识的重复之外,我们在生活中更是无时不刻地进行无意识的重复

比如在搜索电影时,我们实际上是在对着搜索结果——那个列表——重复地检查电影的各个信息(标题、主演、海报等等)直到你看到了你心里一直在默念的那个演员的名字,或者对上了脑子里一直浮现着的海报。

在豆瓣上搜索《蜘蛛侠》

为了便于理解,我们把这个例子再说得细一点。假设你最近才成为漫威粉,对《复仇者联盟 3》里的蜘蛛侠很有好感,看他被灭霸响指了觉得很难过,想看看这个蜘蛛侠的人物电影。在搜索「蜘蛛侠」,搜索结果出来之后,你其实就是在逐个地检查搜索结果,去根据每一条搜索结果提示的东西——标题、主演、海报等等——去找你熟悉的小蜘蛛的形象。

这种重复的含义,其实是「排查」。搜索结果里有若干个《蜘蛛侠》,那么我们就从第一个开始,一个一个按标题、海报、主演排查。这是一种「行进的重复」。

和循环类的重复相比,这种排查类的重复,或者说行进的重复在制作捷径中更为常用。很多人喜欢的复杂动作经常会用到这种重复,它也会是我们教程中的主角1 

在这里,我们先不去看「排查电影搜索结果」这么复杂的例子,先通过一个结构一致,但要简单不少的形式——名单,来认识一下捷径中的重复。

假设我们现在有一些学生的名单2 

庄美胤 女 173
陈柯延 男 172
陈韦僢 男 168
陈元丰 男 167
黄靖飞 男 171
杜雪琪 女 165
黄 敏 女 173
黄利钰 女 162
黄靖杰 男 165
黄湋捷 男 182
林佳欣 女 168
孔德晖 男 167
梁耀勋 男 161

我们要从中筛选出身高 170 公分以上的男生。通过捷径,应该怎么做?

为每个项目重复

这时候我们要用到的,是前面介绍过的第二种重复,排查式重复。它对应的捷径操作是「为每个项目重复」:

操作「为每个项目重复」

大家注意看,这里就没有重复次数了,为什么呢?因为它是在做排查,有几个结果就重复几次,每一次不重样,从头到尾挨个儿过,过完为止。所以它没有重复次数,它的重复次数就是结果的数量。

那么这个特性隐含了一个理解它的关键信息,就是传到「为每个项目重复」这个操作的内容,都最好是有多个结果的(要不然也用不上这个操作)。

一个会产生多个结果的操作,在捷径里,它本质上是产生了一个列表。面对所有产生了多个结果的操作,我们在下面接一个「从列表中选取」再运行,捷径就会让我们从中选择其中一个结果。

这也就是说,「为每个项目重复」这个操作前面要接的内容形式,其实是「列表」。

快速制作文本列表

列表我们已经接触过了,专门就有「列表」这么一个动作。我们的目的是从 13 个人里筛选出所有身高 170 公分以上的男生,那我们是不是要做一个列表,其中有 13 项?

是,也不是。

列表肯定是要做的,但是,不是用「列表」这个操作,那太麻烦了。而且这才 13 个人,要 31 个人呢?300 行那么长的列表也不少见,难道还要一个一个加到捷径里的「列表」操作里?这也太不符合我们这个教程的主旨——自动化了。

捷径里有一个非常「自动化」的制作列表的办法——用文本制作列表。在我们这个需求里,它的做法是:

通过文本制作列表
  1. 复制上面那一串名单和信息
  2. 在捷径里创建一个「文本」操作
  3. 把名单粘贴在「文本」里
  4. 在下面一个「拆分文本」的操作
  5. 「拆分文本」中的「分隔符」我们选择「新行(New Line)」

这样,我们就从由多行文字组成的文本中,获取到了列表形式的结果。能这么做的原理还是我们一直强调的——当一个操作的结果是复数的,那么这些结果就是一个列表。

而这时候,我们就可以在后面接上「为每个项目重复」这个操作。

我们已经说过,「为每个项目重复」这个操作,是排查型重复,也就是一个一个地排查,但每个只排查一次。有了这种方法,再结合我们前面刚刚学过的变量和条件相关知识,我们就能轻松地获取我们想要的结果——身高超过 170 公分的男生。大家注意这其实是两个条件:

  1. 身高:超过 170 公分
  2. 性别:男生

我们可以先从性别下手,也可以先从身高下手。但扫了一眼列表,发现男生很多,但超过 170 公分的人比较少,所以还是先从身高下手。

重复项目

从身高下手,那肯定就是要一个人一个人地检查,谁的身高大于 170 公分了。

一个一个检查,我们已经用「每个项目重复」做到了。怎么来看身高是不是大于 170 公分呢?你可能很快会想到,应该使用我们刚学过的条件操作——「如果」。而且逻辑可能都已经想好了:

如果名字后面的数字大于 170,就保留这个人。小于 170,就不要这个人。

那么问题是我们如何用捷径来实现这个逻辑呢?

首先,「如果」这个操作里的「大于」和「小于」,上一步都只能是数字。这其实很容易理解,汉字怎么比大小嘛。

所以我们首先要解决的问题,是怎么把数字从每一个人的名字和性别后面给提出来。比如说,庄美胤 女 173 这条数据,我们只要最后的 173。

想要做到这一点,其实是想要从一串文字中获取数字。像这样,想要从一大堆字符中,获取到符合自己要求的字符的情况,我们需要用到另一个新的武器——正则表达式。

捷径中关于正则表达式的操作有 3 个,搜索匹配就可以看到它们:

搜索 3 个与正则表达式有关的操作
  • 匹配文本
  • 获取匹配文本的组
  • 替换文本

这三个操作,我们这次只得上用最简单的「匹配文本」3 

把匹配文本拖入到操作列表中,我们就会发现「模式(pattern)」后面已经填入了一个 [0-9a-zA-Z] 这在正则表达式里,意味着匹配一个字母(不论大小写)或数字,而且是第一个字母或数字。这似乎也不难看懂,「0–9」就是数字嘛,「a-z」就是小写数字,而「A-Z」自然就是大写数字。

比如说,在 庄美胤 女 173 这段文本下面接一个匹配文本,「模式」后面我们原封不动地用它里面这个 [0-9a-zA-Z],会出现什么结果呢?会匹配出一个 1。因为 1 是这一行文字中,第一个数字或字母(不分大小写)

相信大家已经完全明白这个 [0-9a-zA-Z] 能干什么了。接下来,你要问了,我们要匹配的不是 1 个数字而是 3 个数字啊。别急,正则表达式非常强大,只有你想不到的匹配方法,没有它匹配不出来的特殊形式的数据。想要把之前的规则进行多次,只要在它后面加一个 + 加号即可,就像这样:

操作「匹配文本」

把 [0-9a-zA-Z] 改为 [0-9a-zA-Z]+ 就可以解决问题。

如果有疑虑,可以只用「文本」和「匹配文本」这两个操作来检查一下是否取得了想要的效果。

验证完毕后,我们重新转回到我们的问题——获取名单中所有身高超过 170 公分的男生。

我们现在已经用「拆分文本」这个操作制作了列表。又在下面接上了「为每个项目重复」来检查每一个数据。考虑到要身高超过 170 公分,我们想到了用「如果」这个操作。但如果要比数值大小,它上一步必须是数字。所以我们用了「匹配文本」和正则表达式,把每一次检查的内容里的数字提取了出来。

在使用「匹配文本」获取了每一条检查的内容里的数字之后,我们就可以用「如果」这个操作来挑选 170 公分以上的人了。在「如果」的「输入」栏中选择「大于」,并且在「数字」里填上 170,看起来就达到了目的。

但是别着急【和条件中的问题一样,如果这里什么也不做,捷径只会获取数字,而不是名单】

要解决这个问题,我们就要用到新的知识——「重复项目」了。

「重复项目」是捷径中内置的变量,不是我们人为设置的,它只会在操作被「重复」操作包裹时出现。出现的位置和剪贴板、当前日期等捷径内置的变量一样,在快速输入栏中。同时,我们也可以使用「获取变量」这个操作直接获取它。而在这里我们使用的也是这种方法:

目前为止的操作组合

目前为止,从「匹配文本」到「获取变量」,是为了告诉捷径:

如果——重复的这一项里的数字——大于 170——那么——获取正在重复的这一项。

拿我们这个捷径来说。当我们第一次运行这个捷径时,理所当然的,「为每个项目重复」检查的第一项数据,就是名单的第一行,也就是 庄美胤 女 173

我们从中获取了数字 173,并和「如果」这个操作里的「大于 170」这个条件进行核对,发现:满足!这时候,我们要告诉捷径,把这行数据给我留下。我们不能只留数字扔了名单。「重复项目」的意义就在于此。

那么接下来就好理解了,「如果」完了,就是「否则」。「如果」里是「大于 170」,满足条件就要。那「否则」后面就肯定是不要了。不要就啥也不填就行了呗?在这不行。

因为其实我们在这一步如果什么也不填,相当于告诉捷径:如果数字大于 170,那么获取「重复项目」,否则获取数字。

为什么呢?因为我们前面给「如果」这个操作输入的就是数字——是我们从每一行数据中匹配出来的身高——我们什么都不干的话那数字就会漏出来。大家可以试试,在「否则」后面什么也不接,结果会出现一串儿 1 开头的 3 位数身高,你如果仔细对比一下,会发现和名单的顺序还挺一致:

如果「否则」后什么也不接会出现的情况

所以我们要解决这个问题,要用到一个新的操作——「无」:

操作「无」以及它在我们这个捷径中的位置

「无」这个操作,如它的简介所说:

此操作什么也不做,也不产生输出。要分隔操作块,或要确保不向下一个操作传递输入时,此操作非常有用。

我们这里就是要确保不向下一个操作传递输入。所以在「否则」里面,要加上这么一个操作「无」。

现在,我们的任务就完成了一半了——我们成功地从一堆身高各异的人中,选出了身高高于 170 公分的。先看一下阶段性胜利的果实:

成功选出了身高高于 170 公分的人

接下来我们要考虑如何选出其中的男生。先告诉大家,选出男生要比选出身高高于 170 公分的人容易多了。

我们继承上一步的成功结果,在「结束重复」之后,再加一个「为每个项目重复」。为什么呢?因为我们现在要为我们上一步获取的结果进行进一步的筛选,把身高高于 170 公分的人筛选到高于 170 的男生,这同样需要针对每条数据筛选。好在我们不用从头筛选了,只从剩下这几个人里挑男生就好。

在「为每个项目重复」下,我们还是放上条件操作「如果」,但这一次不必取数字这么麻烦了,只要在「如果」的「输入」里选择「包含」,在值里填入  就足够:

从身高高于 170 的人中获取男生的操作组合

而且这次我们也不用使用「获取变量」来再取一次「重复条目」,只要在否则后面放上操作「无」就可以了。因为传入「如果」的数据本身就是我们想要的整行的数据4 ,我们只要保证用「无」拦住不符合要求的数据就可以。

所以这次,我们使用的操作和过程就简单了许多。

相信大家注意到我们最后一直加了一个「合并文本」的操作,这个操作我们已经在前一节说过。它的目的是为了把多项结果合并为一个结果。

在这里也要用到它是因为,在我们使用重复相关的操作时,传输进重复操作的肯定是多项结果。随后,只要重复的过程中没把之前进来的多项结果消磨完——比如说一个班里 1 个高于 170 公分的男生都没有——那出来的就依然会是多个结果,多个结果就是列表。这些结果——也就是列表——不会自动合并,需要我们手动合并。

简单来说,进去的时候是列表——不出意外或者没有刻意设计的话——出来的就还是列表。所以要用「合并文本」来把这些结果粘回一起。它和「拆分文本」的作用是刚好相反的,也经常一起出现,可以结合起来一起记忆。

重复索引

到了这一步,我们其实已经完成得很不错了。但是为了 精益求精,我们还可以为名单上每个人名字前加上序号,而这就要用到我们和重复最后一个相关的知识——「重复索引」。

「重复索引」和「重复项目」的属性完全一致:

  • 是捷径中内置的变量,不是我们人为设置的。
  • 只会在操作被「重复」操作包裹时出现。
  • 出现在快速输入栏中,和剪贴板、当前日期等捷径内置的变量一样。
  • 我们也可以使用「获取变量」这个操作直接获取它。

但和「重复项目」不同的是,「重复索引」的是重复运行的次数。一个「重复」操作,运行 1 次,它的「重复索引」就是 1,运行 2 次,它的「重复索引」就是 2,依此类推。

这次我们会把「重复项目」和「重复索引」结合在一起用,大家会非常清楚地看到它们之间的差异。

回想一下我们要做的事,我们现在要把刚才获取的名单每个人前面标上序号,第一个人前面标 1.,第二个人前面标 2.……

要做到这个,我们只需要做到:

  • 第一步:把每行数据提取出来
  • 第二步:按提取的顺序在前面标记相应的数字
  • 第三步:把列表粘成一整片名单

如何做到第一步和第三步?我们已经很了解了:

用于解决第一步和第三步的操作组合

把数据粘贴到「文本」中,接上「拆分文本」,再接上「为每个项目重复」。最后在「结束重复」之后,我们要用「合并文本」,再把名单粘起来。

那么,针对第二步,我们就要用这个「重复索引」了。我们要在「为每个项目重复」和「结束重复」包裹的区域里放 1 个「文本」操作,这 1 个神奇的「文本」操作,就可以解决所有问题:

在重复中添加 1 个「文本」操作

「文本」操作中的内容有 4 个元素:

<重复索引><点><空格><重复项目>
  • 第 1 个元素「重复索引」,就是用来标序号用的,前面介绍过了,运行几次「重复索引」就是几。
  • 第 2 个元素 .,是为了好看,一般有序列表数字后面都有一个这样的点。
  • 第 3 个元素空格,也是为了好看,一般有序列表数字后面那个点的后面,都还会跟一个空格。
  • 第 4 个元素「重复项目」,是我们这一节前面一点的部分的主角,它就是当前正在被重复的那一行数据。

所以这里,我们把「重复索引」和「重复项目」一结合,轻松地就解决了标序号的问题:

展示结果

完整的步骤就是「在重复中添加 1 个『文本』操作」这张图片,非常短。

捷径下载:170 公分男生带序号 

小结

这一节和《条件相关的操作》一样,有一些不容易理解的地方。困难主要在于理解「为每个项目重复」这个操作的性质:

首先,当使用「为每个项目重复」时,我们要保证传进去的是列表,或者换句话说,在「为每个项目重复」这个操作之前的那个操作,它会产生多个结果。

其次,如果不是被检查的项目在「重复」的过程中被消灭干净或者被消灭得只剩一个——比如全班没有一个人、或者只有一个人高于 170 公分——「为每个项目重复」这个操作的结果也往往都是多个的。想要结合它们,要使用拼接结果的手段,是文本的话就使用「合并文本」,是图片的话就使用「拼接图像」。

最后,「为每个项目重复」这个操作并不是一个不会往下传输数据的操作——如果那样的话它就没有价值了——它的工作方式是:

  1. 重复上一步操作产生结果的次数,换句话说,上一步有多少个结果,「为每个项目重复」就会多少次。我们的名单里有 13 个人,我们把名单分成了 13 行,那么这时候「为每个项目重复」就运行 13 次。
  2. 每次向下传递一项数据,第一次就传递第一项数据,第二次就传递第二项数据,直到结束。我们名单里第一行数据是庄美胤 女 173,第二行数据是陈柯延 男 172,那「为每个项目重复」第一次重复时传下去的数据就是庄美胤 女 173,第二次传下去的数据就是陈柯延 男 172,依此类推。

这一周的内容学习过后,相信大家对程序的「死板」有了更深刻的认识。大家肯定在读的时候心里不只说过一次「捷径竟然这么蠢」?变量的部分我猜你想过,条件的部分肯定也想过,重复这里相比也不例外。

但是实际上,这一方面可以看作机器蠢,但另一方面也是机器的精准所在。我们人正是因为不准确的描述加上各种自以为是的揣测,才制造了数不清的误会。

我在教程里,从最开始就在刻意地使用分步骤形式的描述,就是为了将表达清晰化,接近程序能够理解的表达。而这种表达反过来对人类也是更容易精确理解的。

现在大家通过网络看到的语言,大多过于单一但又洗脑,导致很多人的表达能力退化。只会用一些情绪强烈、煽动性强的表达,却丧失了精确表达的能力。

起码,大家在表达一些需要对方明确理解的东西时,可以少向标题党学习,少向网络语言学习,少向哗众取宠的 KOL 们学习;可以接近程序一点、接近机器一点。这样可以更多地减少误会。

只要你在做捷径时会反思「为什么我做的东西捷径好像没搞懂」,这个过程就会对让你的表达更精准有很大的帮助。

练习一

在本节教程里,为了解决「获取身高超过 170 公分的男生」这个问题,我用了 2 次重复,第一次获取了所有身高超过 170 公分的人,第二次从第一次的结果里获取了男生。如何只用一次重复就能做到这一点?(未超纲)

练习二

左:原始数据;右:排序结果

提示:这道练习主干部分没有超纲。但需要用到替换文本,以及多做一些大胆的尝试,比如设定同名变量、重复的嵌套等。排序是很常用的需求,希望大家踊跃尝试。

  • 1循环类的重复的用武之地多是在算法类型的捷径上。这种类型的捷径在遇到现有操作无法实现的算法问题时会发挥作用,但在大多数情况下更接近一种智力挑战。况且很多算法问题,比如累加、求平均值之类的问题,捷径中已经提供了现成的操作——「计算统计数据(Calculate Statistics)」,这更减少了循环类重复的使用场景。
  • 2数据来自谷歌搜索「班级名单」第二个搜索结果,身高数据为虚构数据。
  • 3同时正则表达式我们也不会过多展开地讲,只会在用到的时候解释我们当时使用的正则表达式及其原因。正则表达式并不难学,大家看完这节内容,其实就可以算是入门了。之后再有使用正则表达式的场景,随时用随时查即可。越使用就会越熟练。
  • 4这句话如果读不明白,就说明这一节的一个关键信息没有明白。