MapReduce简介以及F#的实现

MapReduce是一个最先由Google提出的分布式编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map""Reduce",以及他们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。

对于大数据量的计算,通常采用的处理手法就是并行计算,对许多开发人员来说,并行计算还是一个比较遥远的东西。MapReduce它就是一种简化了的并行计算的编程模型,它让那些没有多少并行计算经验的开发人员也可以开发并行应用。在我看来,这也就是MapReduce的价值所在,通过简化编程模型,降低了开发并行应用的入门门槛。相对于现在普通的开发而言,并行计算需要更多的专业知识,有了MapReduce,并行计算就可以得到更广泛的应用。

MapReduce的名字源于这个模型中的两项核心操作:Map和 ReduceMpaReduceMapReduce过程都定义了数据结构(key,value)对,Map操作在一个数据域中用一种类型表达一个数据对,然后在另一个不同的域中返回一个数据队列:

Map(k1,v1) -> list(k2,v2)

Map过程并行应用于每一个输入的数据集,每一次调用都会产生一个(k2,v2)的队列。然后,MapReduce构架收集输出队列中有相同key的数据对,把它们聚集在一起,因为构建了一个不同key的数据对集合。

Reduce方法应用于上面产生的每一个数据对集合,从而产生相同域中的数据集合

Reduce(k2,list(v2)) -> list(v3)

每一个被调用的Reduce方法产生一个v3数据集或者一个空集,这里也可以返回多个value。所有返回的调用结果组成一个结果队列。

MapReduce用在非常广泛的应用程序中,从最简单的统计一篇文章中所有单词出现次数到分布grep、分布排序、web访问日志分析,反向索引构建等。我们可以用多种不同的方式来实现多种不同语意的映射-归并算法,这个算法与其说是一个特定的算法,还不如说是一个算法族。为了更好的理解这一模型,下面利用微软最新推出的函数式编程语言F#来构建一个简单统计单词出现次数的MapReduce

F# MapReduce函数原型:

let map_reduce   

(m:'k1 -> 'v1 -> seq<'k2 * 'v2>)   

(r:'k2 -> seq<'v2> -> 'v3 option)  

: Map<'k1, 'v1> -> Map<'k2, 'v3> =  

map_per_key >> group_by_key >> reduce_per_key

map_reduce 函数接收两个函数:m和r函数。m是映射函数,它的任务是接收k1/v1,并且产生k2/v2队列r是归并函数,当所有的映射函数都退出的时候,归并函数要负责针对每一个键,将它对应的所有值合并在一起。泛型v3是一个累加器,它的初始值是None

Map<'k1, 'v1> -> Map<'k2, 'v3>说明map_reduce函数的参数接收和返回类型。除此之外,重要的要理解后面的三个函数map_per_keygroup_by_keyreduce_per_key,这是map_reduce中的精髓。

map_per_key函数任务是把map转换成seq,然后映射到m函数进行计算,返回的值利用Seq.concat来加入以前统计的结果中;group_by_key函数的任务是统计每个key/value值,把相同key的值合并在一起,返回一个新的key/value中间值队列;reduce_per_key函数的作用是把group_by_key组成的中间值归并,把每个key中的value进行统计。

一个简单的例子:= seq[("s",1);("s",1);("a",1)],应用group_by_key之后就变成了map[("s",seq[1;1]);("a",seq[1])];再应用了reduce_per_key就变成了map[("s",2);("a",1)]

下面分别是三个函数的代码实现:

let map_per_key : Map<'k1, 'v1> -> seq<('k2 * 'v2)> =

      Map.toSeq >> Seq.map (Tuple.uncurry m) >> Seq.concat

  

    let group_by_key (l:seq<('k2 * 'v2)>) : Map<'k2,seq<'v2>> = 

      let insert d (k2, v2) = Map.insert_with Seq.append k2 (seq [v2]) d 

      let func (f:Map<'a, seq<'b>> -> Map<'a, seq<'b>>) (c:'a * 'b)  

        : (Map<'a, seq<'b>> -> Map<'a, seq<'b>>) =  

        fun x -> f(insert x c) 

      (Seq.fold func (fun x -> x) l) Map.empty 

 

    let reduce_per_key : Map<'k2, seq<'v2>> -> Map<'k2,'v3> =

      let unSome k (Some v) = v

      let isSome k = function

        | Some _ -> true

        | None -> false

      Map.map r >> Map.filter isSome >> Map.map unSome  

到目前为止,已经把map_reduce函数介绍完了,下面我们利用map_reduce来做一个单词出现次数的统计程序,下面的代码定义了映射函数m和归并函数r,同时调用map_reduce函数:

let word_occurrence_count : Map<string, string> -> Map<string, int> =

let m = const (String.words >> Seq.map(fun s -> (s, 1)))

let r = const (Seq.sum >> Some)

map_reduce m r

[<EntryPoint>]

let main(args:string array) =

printfn "%A"

  (word_occurrence_count

(Map.empty

  |> Map.add "doc1" "appreciate the unfold"

  |> Map.add "doc2" "fold the fold the unfold"))

System.Console.ReadKey(true) |> ignore

0

当我们执行程序后,显示的结果是:

map [("appreciate", 1); ("fold", 2); ("the", 3); ("unfold", 2)]

在上面简单的例子中,没有使用到并行计算,有兴趣的朋友可以自己做实现

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值