Enumberable中的slice_before, slice_when, slice_after

Enumberable中的三个method。

用途:

把一个enum拆分,最后返回一个enumerator,拆分的位置怎么定?循环enum中的元素,执行给到的block或者Pattern,当返回为true时,该element的位置就是拆分的位置。

slice_before, slice_after非常相似,可以接受block或者Pattern,区别仅在于拆分位置是在匹配元素的前面还是后面,而slice_when 只接受block作为参数,而且block中,需要有两个arguments。

官方文档中,给出了好些例子,感觉有点过于复杂,对我这种新手来说,不大友好:P

这里直接用最简单的例子来说明下它们三个的基本使用。

使用:

[以下代码基于Ruby 2.4]

例子:

demo = [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f']

demo.slice_before { |e| e.is_a?(Integer)}.to_a

# => [[1, "a"], [2, "b", "c"], [3, "d", "e", "f"]]

demo.slice_before(Integer).to_a

# => [[1, "a"], [2, "b", "c"], [3, "d", "e", "f"]]


## after 基本类似,只是从匹配元素后开始拆分
demo.slice_after { |e| e.is_a?(Integer)}.to_a

# => [[1], ["a", 2], ["b", "c", 3], ["d", "e", "f"]]

demo.slice_after(Integer).to_a

# => [[1], ["a", 2], ["b", "c", 3], ["d", "e", "f"]]

demo.slice_when {|x, y| x.is_a?(Integer)}.to_a

# => [[1], ["a", 2], ["b", "c", 3], ["d", "e", "f"]]

这里的Pattern是Integer,有必要提一下Pattern === elt。

slice_before, slice_after可以 使用Pattern === elt, 不过=== 这个method在ruby的不同Object中,有着不一样的定义。ruby doc中的解释是:

Behavior of the === method varies for each Object.

在Object这个类中,===类似于#==,我的理解是, Object中的===,类似于elt.is_a?(Pattern)

比如:

Integer === 1
# => true

Integer === :hello  ## :hello 是一个symbol
# => false

在Regexp正则中,如果elt匹配pattern,则返回true,而在ruby2.5的文档中,对于Set类,===则是includes?的别名方法。如:

Set[1,2,3] === 2 ## true
Set[1,2,3] === 4 ## false

此外,上面的例子中,slice_when的执行结果与slice_after一样,呃,举的例子不好。

slice_when的block中,接受的两个参数,一个是current element,一个是previous element。 它在拆分出连续型数组部分很有用,看例子:

demo = [1, 3, 4, 5, 7, 8, 9, 10, 12]

demo.sort.slice_when { |a, b| a + 1 != b }.to_a

# => [[1], [3, 4, 5], [7, 8, 9, 10], [12]]

也可以用来找出数组中重复数据及其对应的重复个数:

demo = [1, 2, 3, 1, 1, 2, 3, 2, 2, 2, 3, 1]

demo.sort.slice_when(&:!=).map { |x| { x.first => x.count} }

# => [{1=>4}, {2=>5}, {3=>3}]

OK。

参考

Slicing and Dicing Ruby Enumerables

Enumberable

new methods in Ruby 2.2