您的位置:首页 > 其它

【Scala】通过【foldLeft】来判断一个List是否是另一个List的子集

2017-09-01 11:17 288 查看
【Scala】通过【foldLeft】来判断一个List是否是另一个List的子集:
val list1 = "电影 刘德华".split(" ", -1).toList
val list2 = "醉拳 喜剧 刘德华 刘家良 电影 中国香港".split(" ", -1).toList
/**
* 判断list1是否是list2的子集
* @param lst1
* @param lst2
* @return
*/
def list2containslist1(lst1:Seq[String],lst2:Seq[String]):Boolean = {
val initialBoolean = true
(initialBoolean /: lst1) {(x, y) => x && lst2.contains(y)}
}
println(list2containslist1(list1, list2))

我的理解:
(x ,y)中的
y是对lst1的遍历,相当于lst1.foreach(y => ...)
x是表达式(initialBoolean /: lst1) {(x, y) => x && lst2.contains(y)}的返回值,其初始值为initialBoolean = true,initialBoolean是val不可变,但循环迭代的过程是在反复生成新的x
或者也可以采用这种方法:



补充一部分《快学Scala》中关于foldLeft、foldRight的知识
val coll = ... // 某种 Iterable: Seq、List、Set、Map


以不同于集合首元素的初始元素开始计算通常也很有用。对于coll.foldLeft(init)(op)的调用将会计算



例如:
List(1, 7, 2, 9).foldLeft(0)(_ - _)

将得到
0 - 1 - 7 - 2 - 9 = -19
说明:初始值和操作符的两个分开定义的“柯里化”参数,这样Scala就能用初始值的类型来判断出操作符的类型定义。举例来说,在 List(1, 7, 2, 9).foldLeft(" ")(_ + _)中,初始值是一个字符串,因此操作符必定是一个类型定义为(String, Int) => String的函数。
你也可以用/:操作符来写foldLeft操作,就像这样:
(0 /: List(1, 7, 2, 9))(_ - _)

/: 操作符的本意是想让你联想到一棵树的样子。
说明:对/:操作符而言,初始值是第一个操作元。注意,由于操作符以冒号结尾,它是第二个操作元的方法。
Scala同样也提供了foldRight或:\的变体,计算



这些示例看上去并不是很有用。coll.reduceLeft(_ + _)或者coll.foldLeft(0)(_ + _)当然会计算出集合中所有元素之和,但你也可以通过调用coll.sum直接得到这个结果。
折叠有时候可以作为循环的替代,这也是它吸引人的地方。假定我们想要计算某个字符串中字母出现的频率。方式之一是访问每个字母,然后更新一个可变映射。
val freq = scala.collection.mutable.Map[Char, Int]()
for (c <- "Mississippi") freq(c) = freq.getOrElse(c, 0) + 1
//现在freq是Map('i' -> 4, 'M' -> 1, 's' -> 4, 'p' -> 2)

以下是另一种思考方式。在每一步,将频率映射和新遇到的字母结合在一起,产出一个新的频率映射。这就是折叠:



op是什么呢?左操作元是一个部分填充的映射,右操作元是新字母,结果是扩编后的映射。该映射又被作为下一个op调用的输入,最后的结果是包含所有计数的映射。代码是:
(Map[Char, Int]() /: "Mississippi") {
(m, c) => m + (c -> (m.getOrElse(c, 0) + 1))
}

注意这是一个不可变映射,我们在每一步都计算出一个新的映射。
说明:任何while循环都可以用折叠来替代。构建一个把循环中被更新的所有变量结合在一起的数据结构,然后定义一个操作,实现循环中的一步。我并不是说这样做总是好的,但你可能会觉得循环和改值可以像这样被消除是很有趣的一件事。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐