带有rubycollections品/枚举的酷技巧和富有表现力的片段

你最喜欢的ruby系列代码片段是什么? 优选地,它们应该是您的发现,具有表现力,可读性,并在您的编码实践中引入一些乐趣。


数组中的模式匹配(用于局部变量和参数):

(a, b), c = [[:a, :b], :c] [a,b,c] => [:a, :b, :c] (a,), = [[:a]] a => :a 

从非数组分配到多个变量:

 abc, a, b =* "abc".match(/(a)(b)./) => ["abc", "a", "b"] nil1, =* "abc".match(/xyz/) => [] 

使用相同的表达式初始化数组元素:

 5.times.map { 1 } => [1,1,1,1] Array.new(5) { 1 } => [1,1,1,1,1] 

使用相同的值初始化数组:

 [2]*5 =>[2,2,2,2,2] Array.new 5, 2 =>[2,2,2,2,2] 

数组的Sum元素:

 [1,2,3].reduce(0, &:+) => 6 

找到符合条件的所有指数:

 a.each_with_index.find_all { |e, i| some_predicate(e) }.map(&:last) 

替代CSS类:

 (1..4).zip(%w[cls1 cls2].cycle) => [[1, "cls1"], [2, "cls2"], [3, "cls1"], [4, "cls2"]] 

解压:

 keys, values = {a: 1, b: 2}.to_a.transpose keys => [:a, :b] 

探索字符串的布尔成员方法:

 "".methods.sort.grep(/\?/) 

探索特定于字符串的方法:

 "".methods.sort - [].methods 

带有记忆的Lazy Fibonacci系列,取自Neeraj Singh :

 fibs = { 0 => 0, 1 => 1 }.tap do |fibs| fibs.default_proc = ->(fibs, n) { fibs[n] = fibs[n-1] + fibs[n-2] } end fibs.take(10).map(&:last).each(&method(:puts)) 

计数排序的实现:

 module Enumerable def counting_sort(k) reduce(Array.new(k+1, 0)) {|counting, n| counting.tap { counting[n] += 1 }}. map.with_index {|count, n| [n] * count }.flatten end end 

sum又名前缀sum的实现:

 module Enumerable def scan(initial=nil, sym=nil, &block) args = if initial then [initial] else [] end unless block_given? args, sym, initial = [], initial, first unless sym block = ->(acc, el) { acc.send(sym, el) } end [initial || first].tap {|res| reduce(*args) {|acc, el| block.(acc, el).tap {|e| res << e } } } end end 

在这里,我尝试使用Hash#each产生KeyValuePair而不是两个元素的Array 。 在做了这样一个残酷的猴子补丁之后,有多少代码仍然有用,这是相当令人惊讶的。 好吧,鸭子打字!

 class Hash KeyValuePair = Struct.new(:key, :value) do def to_ary return key, value end end old_each = instance_method(:each) define_method(:each) do |&blk| old_each.bind(self).() do |k, v| blk.(KeyValuePair.new(k, v)) end end end 

我一直在玩的东西是让Enumerable#===执行递归结构模式匹配。 我不知道这是否有用。 我甚至不知道它是否真的有效。

 module Enumerable def ===(other) all? {|el| next true if el.nil? begin other.any? {|other_el| el === other_el } rescue NoMethodError => e raise unless e.message =~ /any\?/ el === other end } end end 

我最近玩弄的另一件事是重新实现Enumerable所有方法,但是使用reduce而不是each作为基础。 在这种情况下,我知道它实际上没有正常工作。

 module Enumerable def all? return reduce(true) {|res, el| break false unless res; res && el } unless block_given? reduce(true) {|res, el| break false unless res; res && yield(el) } end def any? return reduce(false) {|res, el| break true if res || el } unless block_given? reduce(false) {|res, el| break true if res || yield(el) } end def collect reduce([]) {|res, el| res << yield(el) } end alias_method :map, :collect def count(item=undefined = Object.new) return reduce(0) {|res, el| res + 1 if el == item } unless undefined.equal?(item) unless block_given? return size if respond_to? :size return reduce(0) {|res, el| res + 1 } end reduce(0) {|res, el| res + 1 if yield el } end def detect(ifnone=nil) reduce(ifnone) {|res, el| if yield el then el end unless res } end alias_method :find, :detect def drop(n=1) reduce([]) {|res, el| res.tap { res << el unless n -= 1 >= 0 }} end def drop_while reduce([]) {|res, el| res.tap { res << el unless yield el }} end def each tap { reduce(nil) {|_, el| yield el }} end def each_with_index tap { reduce(-1) {|i, el| (i+1).tap {|i| yield el, i }}} end def find_all reduce([]) {|res, el| res.tap {|res| res << el if yield el }} end alias_method :select, :find_all def find_index(item=undefined = Object.new) return reduce(-1) {|res, el| break res + 1 if el == item } unless undefined.equals?(item) reduce(-1) {|res, el| break res + 1 if yield el } end def grep(pattern) return reduce([]) {|res, el| res.tap {|res| res << el if pattern === el }} unless block_given? reduce([]) {|res, el| res.tap {|res| res << yield(el) if pattern === el }} end def group_by reduce(Hash.new {|hsh, key| hsh[key] = [] }) {|res, el| res.tap { res[yield el] = el }} end def include?(obj) reduce(false) {|res, el| break true if res || el == obj } end def reject reduce([]) {|res, el| res.tap {|res| res << el unless yield el }} end end 

从数组初始化多个值:

 a = [1,2,3] b, *c = a assert_equal [b, c], [1, [2,3]] d, = a assert_equal d, a[0] 

我自己是:

使用相同的表达式初始化数组元素:

 5.times.map { some_expression } 

初始化具有相同值的数组:

 [value]*5 

数组的Sum元素:

 [1,2,3].reduce(0, &:+) 

找到符合条件的所有指数:

 a.each_with_index.find_all { |e, i| some_predicate(e) }.map(&:last) 

不是真正的片段,但我喜欢这些通用结构(我只展示如何使用它们,实现很容易在网上找到)。

转换数组 – >哈希( to_hashmash ,想法是一样的,参见Facets实现):

 >> [1, 2, 3].mash { |k| [k, 2*k] } => {1=>2, 2=>4, 3=>6} 

Map + select / detect:你想做一个地图并只得到第一个结果(所以map { ... }.first效率低下):

 >> [1, 2, 3].map_select { |k| 2*k if k > 1 } => [4, 6] >> [1, 2, 3].map_detect { |k| 2*k if k > 1 } => 4 

延迟迭代(lazy_map,lazy_select,…)。 例:

 >> 1.upto(1e100).lazy_map { |x| 2 *x }.first(5) => [2, 4, 6, 8, 10] 

计算满足一个条件或另一个条件的项目数:

 items.count do |item| next true unless first_test?(item) next true unless second_test?(item) false end 

count表示你不必做i = 0i += 1

next意味着你可以完成块的迭代并仍然提供答案,而不是一直闲逛直到结束。

(如果你想,你可以用单行代替块的最后两行! second_test?(item) ,但这会让它看起来更麻烦)

探索字符串的布尔成员方法:

 "".methods.sort.grep(/\?/) 

探索特定于字符串的方法:

 "".methods.sort - [].methods