Mark Chang's Blog

Machine Learning, Deep Learning and Python

Ruby Enumerable 1 : Collect , Inject

Introduction

ruby 裡面, 也有類似 functional programming style 的東西, 像是 map , reduce 之類的

但是這些不是像 python 那樣是屬於 build in function , 像這樣

1
2
>>> map(lambda x:x*2 ,[1,2,3,4])
[2, 4, 6, 8]

ruby 裡面的 map 是這樣, 如下

1
2
irb(main):001:0> [1,2,3,4].map{|x| x*2}
=> [2, 4, 6, 8]

它是 Enumerable TypeMethod

除此之外, mapreduceruby 裡面還有其他的別名, 就是 collectinject

collect and map

給定一個 array, a1

1
2
irb(main):010:0> a1 = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]

collectmap 的意思是一樣的

我們如果要把每個元素乘上三再加二, 可以用以下方法

1
2
3
4
irb(main):011:0> a1.collect{|x| x*3+2 }
=> [5, 8, 11, 14, 17]
irb(main):012:0> a1.map{|x| x*3+2 }
=> [5, 8, 11, 14, 17]

python 不同的是, python 只可以寫 in-lineanonymous function , 如果要寫多行的, 就要重新寫個 def function():

ruby 可以直接寫多行的 anonymous function , 方法如下

1
2
3
4
5
6
7
8
9
10
irb(main):004:0> a1.collect do |x|
irb(main):005:1* y = x+3
irb(main):006:1> x = y*2
irb(main):007:1> end
=> [8, 10, 12, 14, 16]
irb(main):008:0> a1.map do |x|
irb(main):009:1* y = x+3
irb(main):010:1> x = y*2
irb(main):011:1> end
=> [8, 10, 12, 14, 16]

collect_concat and flat_map

如果我們要處理的是 array of array

1
2
irb(main):012:0> a2 = [[-1, 5], [-2, 8], [-3, 11], [-4, 14], [-5, 17]]
=> [[-1, 5], [-2, 8], [-3, 11], [-4, 14], [-5, 17]]

希望做完運算以後可以把結果合併成同一個 array , 而不是array of array , 用 collection 就不夠了

1
2
irb(main):013:0> a2.collect{|x| [-x,x*3+2] }
=> [[-1, 5], [-2, 8], [-3, 11], [-4, 14], [-5, 17]]

這時就要用 collect_concatflat_map

1
2
3
4
irb(main):014:0> a2.collect_concat{|x| [-x,x*3+2] }
=> [-1, 5, -2, 8, -3, 11, -4, 14, -5, 17]
irb(main):015:0> a2.flat_map{|x| [-x,x*3+2] }
=> [-1, 5, -2, 8, -3, 11, -4, 14, -5, 17]

inject and reduce

假設有這樣一個 string of array , 從 ‘2’ 到 ‘6’

1
2
irb(main):033:0> a3 = ['2','3','4','5','6']
=> ["2", "3", "4", "5", "6"]

如果我們希望把同一個 array 中所有的 string 都合併起來, 可以用 + 這個 operation , 進行 reduce, 或是寫 anonymous functioninject

1
2
3
4
irb(main):034:0> a3.reduce(:+)
=> "23456"
irb(main):035:0> a3.inject { |sum, n| sum + n }
=> "23456"

如果起始的第一個 element , 不在 array 中, 例如 1 不在 a3 裡面, 則可以用以下方法

1
2
3
4
irb(main):036:0> a3.reduce('1',:+)
=> "123456"
irb(main):037:0> a3.inject('1'){ |sum, n| sum + n }
=> "123456"

當然, reduceinject 也可以寫成 multiple lineanonymous function , 如下

1
2
3
4
5
6
7
8
irb(main):039:0> %w{ cat sheep bear }.reduce do |memo, word|
irb(main):040:1*    memo.length > word.length ? memo : word
irb(main):041:1> end
=> "sheep"
irb(main):042:0> %w{ cat sheep bear }.inject do |memo, word|
irb(main):043:1*    memo.length > word.length ? memo : word
irb(main):044:1> end
=> "sheep"

Reference

http://www.ruby-doc.org/core-2.1.0/Enumerable.html

Comments