scala迭代器_Scala教程–迭代,用于表达式,产量,图,过滤器,计数

scala迭代器

前言

这是初学者接触Scala的教程的第4部分。 该博客上还有其他文章,您可以在我正在为其创建的计算语言学课程的链接页面上找到这些链接和其他资源。 此外,您可以在“ JCG Java教程”页面上找到本教程和其他教程系列。

本教程背离了前三个教程的初学者性质,因此对于已经具有另一种语言编程经验的读者来说,这可能会更加有趣。 (不过,另请参阅第3部分中有关在Scala中使用匹配的部分 。)

迭代,Scala方式

到目前为止,我们已经(大多数)通过使用它们的索引访问列表中的单个项目。 但是,与列表相关的最自然的事情之一就是对列表中的每个项目重复执行某些操作,例如:“对于给定单词列表中的每个单词:打印它”。 这是怎么说在Scala。

scala> val animals = List("newt", "armadillo", "cat", "guppy")
animals: List[java.lang.String] = List(newt, armadillo, cat, guppy)
 
scala> animals.foreach(println)
newt
armadillo
cat
guppy

这表示采用列表的每个元素(由foreach表示)并按顺序对其应用函数(在本例中为println )。 发生了一些规格不足,因为我们没有提供变量来命名元素。 在某些情况下(例如上述情况),这是可行的,但并非总是可能的。 这是完整的外观,带有一个变量命名元素。

scala> animals.foreach(animal => println(animal))
newt
armadillo
cat
guppy

当您需要做更多事情时,例如将一个String元素与另一个String串联时,这很有用。

scala> animals.foreach(animal => println("She turned me into a " + animal))
She turned me into a newt
She turned me into a armadillo
She turned me into a cat
She turned me into a guppy

或者,如果您正在使用它执行计算,例如输出字符串列表中每个元素的长度。

scala> animals.foreach(animal => println(animal.length))
4
9
3
5

我们可以使用for表达式获得与foreach相同的结果。

scala> for (animal <- animals) println(animal.length)
4
9
3
5

到目前为止,我们一直在做的事情,这两种表示对List元素进行迭代的方式是等效的。 但是,它们是不同的: for表达式返回一个值,而foreach仅对列表的每个元素执行某些功能。 后一种用法称为副作用 :通过打印每个元素,我们不会创建新的值,而只是在每个元素上执行操作。 使用for表达式 ,我们可以产生创建转换后的列表的值。 例如,使用println与以下内容进行对比。

scala> val lengths = for (animal <- animals) yield animal.length
lengths: List[Int] = List(4, 9, 3, 5)

结果是一个新列表,其中包含动物列表中每个元素的长度(字符数)。 (当然,您现在可以通过执行lengths.foreach(println)来打印其内容,但是通常我们希望使用这种值来执行其他通常更有趣的事情。)

我们刚才所做的是使用函数length一对一地将动物的值映射到一组新的值中。 列表还有另一个名为map的功能,可以直接执行此操作。

scala> val lengthsMapped = animals.map(animal => animal.length)
lengthsMapped: List[Int] = List(4, 9, 3, 5)

因此, 让步表达式map方法获得相同的输出,并且在许多情况下它们几乎是等效的。 但是,使用map通常更方便,因为您可以轻松地将一系列操作链接在一起。 例如,假设您要在数字列表中加1,然后求平方,因此将List(1,2,3)变成List(2,3,4)变成List(4,9,16 )。 您可以使用地图轻松地做到这一点。

nums.map(x=>x+1).map(x=>x*x)

一些读者会对刚做的事情感到困惑。 此处更明确地使用中间变量nums2存储附加列表。

scala> val nums2 = nums.map(x=>x+1)
nums2: List[Int] = List(2, 3, 4)
 
scala> nums2.map(x=>x*x)
res9: List[Int] = List(4, 9, 16)

由于nums.map(x => x + 1)返回一个List,因此我们不必将其命名为变量即可使用它-我们可以立即使用它,包括对其执行另一个map函数。 (当然,可以一次执行该计算,例如map((x + 1)*(x + 1)),但通常一个人正在使用一系列内置函数,或者一个函数已经预定义了) 。

您可以继续映射到您的心脏内容,包括从Ints到Strings的映射。

scala> nums.map(x=>x+1).map(x=>x*x).map(x=>x-1).map(x=>x*(-1)).map(x=>"The answer is: " + x)
res12: List[java.lang.String] = List(The answer is: -3, The answer is: -8, The answer is: -15)

注意:在所有这些情况下使用x都不重要。 它们可以分别命名为x,y,zturlingdromes42-任何有效的变量名。

遍历多个列表

有时,您有两个配对的列表,并且您需要同时对每个列表中的元素进行操作。 例如,假设您有一个单词标记列表和另一个带有词性的列表。 (有关语音部分的讨论,请参见前面的教程 。)

scala> val tokens = List("the", "program", "halted")
tokens: List[java.lang.String] = List(the, program, halted)
 
scala> val tags = List("DT","NN","VB")
tags: List[java.lang.String] = List(DT, NN, VB)

现在,假设我们要将它们输出为以下字符串:

/ DT程序/ NN停止/ VB

最初,我们将一步一步地进行操作,然后说明如何在一行中完成所有操作。

首先,我们使用zip函数将两个列表放在一起,并从每个列表中获得一个新的成对元素列表。

scala> val tokenTagPairs = tokens.zip(tags)
tokenTagPairs: List[(java.lang.String, java.lang.String)] = List((the,DT), (program,NN), (halted,VB))
 
Zipping two lists together in this way is a common pattern used for iterating over two lists.
 
Now we have a list of token-tag pairs we can use a for expression to turn it into a List of strings.
 
1
scala> val tokenTagSlashStrings = for ((token, tag) <- tokenTagPairs) yield token + "/" + tag
tokenTagSlashStrings: List[java.lang.String] = List(the/DT, program/NN, halted/VB)

现在,我们只需要通过将所有字符串元素之间留有空格的方式将字符串列表转换为单个字符串即可。 函数mkString使此操作变得容易。

scala> tokenTagSlashStrings.mkString(" ")
res19: String = the/DT program/NN halted/VB

最后,这一切都一步一步完成。

scala> (for ((token, tag) <- tokens.zip(tags)) yield token + "/" + tag).mkString(" ")
res23: String = the/DT program/NN halted/VB

将字符串翻录成有用的数据结构

在计算语言学中,通常需要将字符串输入转换为有用的数据结构。 考虑上一教程中提到的词性标记的句子。 首先,将其分配给变量sendRaw。

val sentRaw = "The/DT index/NN of/IN the/DT 100/CD largest/JJS Nasdaq/NNP financial/JJ stocks/NNS rose/VBD modestly/RB as/IN well/RB ./."

现在,让我们将其转换为“元组列表”,其中每个元组的第一个元素为单词,第二个元素为postag。 我们从执行此操作的一行开始,以便您可以看到所需的结果,然后我们将详细检查每个步骤。

scala> val tokenTagPairs = sentRaw.split(" ").toList.map(x => x.split("/")).map(x => Tuple2(x(0), x(1)))
tokenTagPairs: List[(java.lang.String, java.lang.String)] = List((The,DT), (index,NN), (of,IN), (the,DT), (100,CD), (largest,JJS), (Nasdaq,NNP), (financial,JJ), (stocks,NNS), (rose,VBD), (modestly,RB), (as,IN), (well,RB), (.,.))

让我们依次介绍一下这些内容。 第一个分割在每个空格字符处剪切sendRaw ,并返回一个字符串数组,其中每个元素都是空格之间的材料。

scala> sentRaw.split(" ")
res0: Array[java.lang.String] = Array(The/DT, index/NN, of/IN, the/DT, 100/CD, largest/JJS, Nasdaq/NNP, financial/JJ, stocks/NNS, rose/VBD, modestly/RB, as/IN, well/RB, ./.)

什么是数组? 这是一种序列,类似于List,但是它具有一些不同的属性,我们将在后面讨论。 现在,让我们继续使用列表,我们可以使用toList方法来完成。 另外,让我们将其分配给变量,以使其余操作更易于关注。

scala> val tokenTagSlashStrings = sentRaw.split(" ").toList
tokenTagSlashStrings: List[java.lang.String] = List(The/DT, index/NN, of/IN, the/DT, 100/CD, largest/JJS, Nasdaq/NNP, financial/JJ, stocks/NNS, rose/VBD, modestly/RB, as/IN, well/RB, ./.)

现在,我们需要将该列表中的每个元素转换为标记和标记对。 让我们首先考虑一个元素,将类似“ The / DT ”的东西变成一对( “ The”,“ DT”) 。 下一行显示了如何使用中间变量一次完成这一步骤。

scala> val first = "The/DT"
first: java.lang.String = The/DT
 
scala> val firstSplit = first.split("/")
firstSplit: Array[java.lang.String] = Array(The, DT)
 
scala> val firstPair = Tuple2(firstSplit(0), firstSplit(1))
firstPair: (java.lang.String, java.lang.String) = (The,DT)

因此, firstPair是一个元组,代表首先在字符串中编码的信息。 这涉及两个操作,拆分,然后从拆分产生的Array中创建一个元组。 我们可以使用map对tokenTagSlashStrings中的所有元素执行此操作。 首先让我们将字符串转换为数组。

scala> val tokenTagArrays = tokenTagSlashStrings.map(x => x.split("/"))
res0: List[Array[java.lang.String]] = List(Array(The, DT), Array(index, NN), Array(of, IN), Array(the, DT), Array(100, CD), Array(largest, JJS), Array(Nasdaq, NNP), Array(financial, JJ), Array(stocks, NNS), Array(rose, VBD), Array(modestly, RB), Array(as, IN), Array(well, RB), Array(., .))

最后,我们将Arrays转换为Tuple2s,并获得我们之前使用单线获得的结果。

scala> val tokenTagPairs = tokenTagArrays.map(x => Tuple2(x(0), x(1)))
tokenTagPairs: List[(java.lang.String, java.lang.String)] = List((The,DT), (index,NN), (of,IN), (the,DT), (100,CD), (largest,JJS), (Nasdaq,NNP), (financial,JJ), (stocks,NNS), (rose,VBD), (modestly,RB), (as,IN), (well,RB), (.,.))

注意 :如果您愿意使用将一系列操作链接在一起的单线,则一定要使用它们。 但是,如果使用几行涉及一堆中间变量的行可以帮助您分解任务并获得所需的结果,则不会感到羞耻。

拥有对列表(Tuple2s)的非常有用的事情之一是, 解压缩功能为我们提供了两个列表,一个包含所有第一个元素,另一个包含所有第二个元素。

scala> val (tokens, tags) = tokenTagPairs.unzip
tokens: List[java.lang.String] = List(The, index, of, the, 100, largest, Nasdaq, financial, stocks, rose, modestly, as, well, .)
tags: List[java.lang.String] = List(DT, NN, IN, DT, CD, JJS, NNP, JJ, NNS, VBD, RB, IN, RB, .)

有了这个,我们就转了一圈。 从原始字符串开始(例如我们很可能从文本文件中读取),现在我们有了Lists,使我们能够进行有用的计算,例如将这些标签转换为另一种形式。

提供您已定义的映射功能

让我们返回上一教程中进行的简化postag的练习。 我们将对其进行一些修改:不是缩短Penn Treebank的词性,而是使用大多数人都熟悉的英语单词(例如名词和动词)将它们转换为课程的词性。 以下函数将Penn Treebank标记转换为这些课程标记,以提供比上一教程中介绍的标记更多的标记(请注意:这仍然是不完整的,但仅用于说明这一点)。

def coursePos (tag: String) = tag match {
  case "NN" | "NNS" | "NNP" | "NNPS"                       => "Noun"
  case "JJ" | "JJR" | "JJS"                                => "Adjective"
  case "VB" | "VBD" | "VBG" | "VBN" | "VBP" | "VBZ" | "MD" => "Verb"
  case "RB" | "RBR" | "RBS" | "WRB" | "EX"                 => "Adverb"
  case "PRP" | "PRP$" | "WP" | "WP$"                       => "Pronoun"
  case "DT" | "PDT" | "WDT"                                => "Article"
  case "CC"                                                => "Conjunction"
  case "IN" | "TO"                                         => "Preposition"
  case _                                                   => "Other"
}

现在,我们可以将此功能映射到先前获得的集合中的语音部分。

scala> tags.map(coursePos)
res1: List[java.lang.String] = List(Article, Noun, Preposition, Article, Other, Adjective, Noun, Adjective, Noun, Verb, Adverb, Preposition, Adverb, Other)

瞧! 如果我们要以这种方式转换标签,然后像开始时那样将它们输出为字符串,则只需几个步骤。 我们将从头开始并回顾一下。 尝试自己运行以下内容。

val sentRaw = "The/DT index/NN of/IN the/DT 100/CD largest/JJS Nasdaq/NNP financial/JJ stocks/NNS rose/VBD modestly/RB as/IN well/RB ./."
 
val (tokens, tags) = sentRaw.split(" ").toList.map(x => x.split("/")).map(x => Tuple2(x(0), x(1))).unzip
 
tokens.zip(tags.map(coursePos)).map(x => x._1+"/"+x._2).mkString(" ")

还有一点是,当您提供(x => x + 1)这样的表达式来映射时 ,实际上是在定义一个匿名函数! 这是具有不同规格级别的相同地图操作

scala> val numbers = (1 to 5).toList
numbers: List[Int] = List(1, 2, 3, 4, 5)
 
scala> numbers.map(1+)
res11: List[Int] = List(2, 3, 4, 5, 6)
 
scala> numbers.map(_+1)
res12: List[Int] = List(2, 3, 4, 5, 6)
 
scala> numbers.map(x=>x+1)
res13: List[Int] = List(2, 3, 4, 5, 6)
 
scala> numbers.map((x: Int) => x+1)
res14: List[Int] = List(2, 3, 4, 5, 6)

因此,这都是一致的:无论您传入命名函数还是匿名函数, map都会将其应用于列表中的每个元素。

最后,请注意,您可以使用最终形式定义函数。

scala> def addOne = (x: Int) => x + 1
addOne: (Int) => Int
 
scala> addOne(1)
res15: Int = 2

这类似于我们之前定义的函数(例如def addOne(x:Int)= x + 1 ),但是在某些情况下更方便,我们将在后面介绍。 现在,要实现的事情是,每当映射时,您要么使用已存在的功能,要么即时创建一个功能。

过滤和计数

映射方法是一种对List的每个元素执行计算的便捷方法,可以有效地将List从一组值转换为具有从每个对应元素计算出的一组值的新List。 还有更多的方法具有其他作用,例如从List( 过滤器 )中删除元素,对满足给定谓词的元素数( count )进行计数 ,以及从List中的所有元素计算合计单个结果( reducefold) )。 让我们考虑一个简单的任务:计算在标记的句子中有多少标记不是名词或形容词。 首先,让我们从之前获取映射的postag列表。

scala> val courseTags = tags.map(coursePos)
courseTags: List[java.lang.String] = List(Article, Noun, Preposition, Article, Other, Adjective, Noun, Adjective, Noun, Verb, Adverb, Preposition, Adverb, Other)

一种方法是过滤掉所有名词和形容词,以获得没有它们的列表,然后获取其长度。

scala> val noNouns = courseTags.filter(x => x != "Noun")noNouns: List[java.lang.String] = List(Article, Preposition, Article, Other, Adjective, Adjective, Verb, Adverb, Preposition, Adverb, Other)
 
scala> val noNounsOrAdjectives = noNouns.filter(x => x != "Adjective")
noNounsOrAdjectives: List[java.lang.String] = List(Article, Preposition, Article, Other, Verb, Adverb, Preposition, Adverb, Other)
 
scala> noNounsOrAdjectives.length
res8: Int = 9

但是,由于filter仅采用布尔值,因此我们当然可以使用布尔合取和析取来简化事物。 并且,我们不需要保存中间变量。 这是一个班轮。

scala> courseTags.filter(x => x != "Noun" && x != "Adjective").length
res9: Int = 9

如果我们想要的只是元素的数量,我们可以只使用带有相同谓词的count

scala> courseTags.count(x => x != "Noun" && x != "Adjective")
res10: Int = 9

作为练习,尝试执行以sendRaw开头并提供值“ resX:Int = 9 ”(其中X是您在Scala REPL中得到的值)的单线

在下一个教程中,我们将看到如何使用reducefold来计算List的聚合结果。

参考: Scala中的入门程序员的第一步,来自BCG博客的JCG合作伙伴 Jason Baldridge的 第4部分

相关文章 :


翻译自: https://www.javacodegeeks.com/2011/10/scala-tutorial-iteration-for.html

scala迭代器

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值