mapreduce练习_MapReduce风格的练习

mapreduce练习

在上一期《编程风格练习》中,我们使用Hazelcast库解决了词频问题。 这次,我们将为此使用MapReduce方法。

这是《编程风格练习》重点系列的 19 帖子。其他帖子包括:

  1. 以编程风格介绍练习
  2. 以编程风格进行练习,将内容堆叠起来
  3. 编程风格的练习,Kwisatz Haderach风格
  4. 编程风格的练习,递归
  5. 具有高阶功能的编程风格的练习
  6. 以编程风格进行练习
  7. 以编程风格进行练习,回到面向对象的编程
  8. 编程风格的练习:地图也是对象
  9. 编程风格的练习:事件驱动的编程
  10. 编程风格的练习和事件总线
  11. 反思编程风格的练习
  12. 面向方面的编程风格的练习
  13. 编程风格的练习:FP&I / O
  14. 关系数据库风格的练习
  15. 编程风格的练习:电子表格
  16. 并发编程风格的练习
  17. 编程风格的练习:在线程之间共享数据
  18. 使用Hazelcast以编程风格进行练习
  19. MapReduce风格的练习 (本篇文章)
  20. 编程风格的练习总结

简而言之MapReduce

MapReduce是一个包含两个步骤的过程:

  1. Map :执行转换,过滤和分类为不同的“队列”
  2. 减少 :将“队列”的内容汇总到结果中

MapReduce的最大的好处是,既映射减少步骤可以并行的两个潜在的执行。 这非常适合处理大型数据集。

下图有助于可视化整体流程:

MapReduce活动图

虽然MapReduce使并行成为可能,但实现它不是强制性的: 并行只是一个选择 。 原始Python代码和Kotlin端口均未使用它。

迁移到Kotlin

参考代码使用Python yield关键字:函数不会返回简单的项集合 ,而是返回 。 尽管代码看起来完全一样,但实际情况却有所不同。 与标准集合相比,与流中关联的项目数成比例地提高了性能。 在Java中,使用Stream实现Stream ,而在Kotlin中,使用Sequence

为此,Kotlin提供了Iterable<T>.asSequence()扩展功能。 要更深入地了解Kotlin中的集合和序列,以及它们与Java流的关系,请查看此早期文章

应用于眼前的问题,处理管道如下所示:

数据处理管道,从文件名到哈希图

这将转换为以下代码:

funrun(filename:String):Map<String,Int>=read(filename)
  .asSequence()
  .chunked(200)
  .map(::splitWords)
  .reduce{acc,pair->countWords(acc,pair)}
  .sortedBy{it.second}
  .takeLast(25)
  .toMap()

还原概述

map()函数已在本系列的先前文章中得到了充分使用。 此外,我相信大多数开发人员已经以某种方式熟悉它。

另一方面,即使使用了很多方法, reduce()函数的理解也很少。 例如,在Java中,流的终端操作是归约函数:这些函数包括sum()average()max()以及collect() ! 从collect() Javadoc:

使用Collector在此流的元素上执行可变还原操作。

收集函数只是Stream提供的更通用的reduce()函数的特化:

流类图专注于reduce

如图所示,减少有三种“味道”:

  1. 第一种形式接受单个BinaryOperator参数。 BinaryOperator<T>接受两个T类型的参数,并将它们组合在一起以返回T 请注意,由于起始流可能为空,因此该方法返回Optional<T>

    例如,以下代码段汇总了流中的项目:

    Stream.of(1,2,3,4,5)
          .reduce{a,b->a+b}
  2. 第二种风味类似于第一种,但需要一个起始值。 因此,返回类型不是Optional<T>而是T 如果流为空,则结果将为起始值。 如果不是,它应该与先前的风味相同,但前提是起始值是还原功能的中性元素。
    Stream.of(1,2,3,4,5)
          .reduce{a,b->a+b}
  3. 第三种也是最后一种风味允许以更复杂的代价更改返回的类型

在Kotlin中,还原与Java中的还原非常相似。 但是,有一点区别:接受起始值的函数称为fold() ,不接受的函数称为reduce() 。 另外,它们两个都还提供了另一个签名,该签名提供了当前项目的索引。

签名 起始值 索引

<S, T : S> reduce(op: (S, T) → S): S

<S, T : S> reduceIndexed(op: (Int, S, T) → S): S

<T, R> fold(initial: R, op: (R, T) → R): R

<T, R> foldIndexed(initial: R, op: (Int, R, T) → R): R

上面处理空序列的无起始值函数将在运行时引发异常。 这是我认为Kotlin API与Jav​​a相比显得苍白的少数地方之一。 我对Kotlin的Slack提出了要点,可能在stdlib的未来版本之一中引入了新的xxxOrNull()。

Kotlin中已实现的功能

映射函数仅从行列表中创建对数为1的单词对:

funsplitWords(lines:Iterable<String>):Iterable<Pair<String,Int>>{
  funIterable<String>.scan()=this
    .flatMap{it.toLowerCase().split("\\W|_".toRegex())}
    .filter{it.isNotBlank()&&it.length>=2}
  funIterable<String>.removeStopWords():Iterable<String>{
    valstopWords=read("stop_words.txt")
      .flatMap{it.split(",")}
    returnthis-stopWords
  }
  returnlines.scan()
    .removeStopWords()
    .map{itto1}
}

我相信这很简单。 还原功能结合了字频率的两个iterables共同创造一个新的迭代。

funcountWords(frequencies1:Iterable<Pair<String,Int>>,
               frequencies2:Iterable<Pair<String,Int>>):Iterable<Pair<String,Int>>{
  valresults=mutableMapOf<String,Int>()
  frequencies1.forEach{
    results.merge(it.first,it.second){
      count,value->count+value
    }
  }
  frequencies2.forEach{
    results.merge(it.first,it.second){
      count,value->count+value
    }
  }
  returnresults.toList()
}

这很粗糙,但是行得通。 可悲的是,我发现没有其他优雅的方法可以实现合并。 提案欢迎!

一路上有点打ic

那可能是终点,但是不幸的是不是。 对于最小的数据集-每个单词的频率为一个,对于最大的数据集-自豪和偏见,大多数测试成功。 但是,它对于以下示例失败:

White tigers live mostly in India
Wild lions live mostly in Africa

其背后的原因是,映射函数将默认频率分配为1,但是读取的行被分成大小为200的单词块(请参见上文)。 由于样本不够大,因此未应用 countWords() 减少函数 。 因此,单词对/ 1不会合并在一起,并且每个单词的结果映射图的频率为1。

为了返回正确的结果,应该先检查样本大小,然后再通过MapReduce流程将其发送出去。

结论

MapReduce没什么大不了的。 大多数开发人员已经在不知情的情况下使用它。 唯一的挑战是编码正确的映射和归约函数。

另外,应该特别注意输入的大小和处理方式,因为如果输入的大小太小,则不需要缩减函数,也不会执行其代码。 但是,在大多数情况下,这不应该成为问题,因为MapReduce的目标是大型数据集。

这篇文章的完整源代码可以在Github上找到。

翻译自: https://blog.frankel.ch/exercises-programming-style/19/

mapreduce练习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值