带有 ruby​​ 集合/枚举的炫酷技巧和富有表现力的片段

发布于 2024-10-05 05:30:29 字数 1172 浏览 0 评论 0原文

您最喜欢的 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]

数组的元素求和:

[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

What are your favorite code snippets with ruby collections? Preferably they should be discovery for you, be expressive, readable and introduce some fun in your coding practice.


Pattern-matching in arrays (for local variables and parameters):

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

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

Assigning from non-arrays to multiple variables:

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

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

Initialize array elements with the same expression:

5.times.map { 1 }    
=> [1,1,1,1]

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

Initialize array with the same value:

[2]*5
=>[2,2,2,2,2]

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

Sum elements of an array:

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

=> 6

Find all indices that match condition:

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

Alternate CSS classes:

(1..4).zip(%w[cls1 cls2].cycle)

=> [[1, "cls1"], [2, "cls2"], [3, "cls1"], [4, "cls2"]]

Unzipping:

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

Exploring boolean member methods of a string:

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

Exploring string-specific methods:

"".methods.sort - [].methods

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

刘备忘录 2024-10-12 05:30:29

带记忆功能的惰性斐波那契数列,取自 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 又名前缀和的实现:

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

Lazy Fibonacci series with memoization, taken from 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))

An implementation of Counting Sort:

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

An implementation of sum aka prefix 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

Here, I experimented with having Hash#each yield KeyValuePairs instead of two-element Arrays. It's quite surprising, how much code still works, after doing such a brutal monkey-patch. Yay, duck typing!

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

Something I have been playing around with is making Enumerable#=== perform recursive structural pattern matching. I have no idea if this is in any way useful. I don't even know if it actually works.

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

Another thing I toyed around with recently was re-implementing all methods in Enumerable, but using reduce instead of each as the basis. In this case, I know it doesn't actually work properly.

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
笑,眼淚并存 2024-10-12 05:30:29

从数组初始化多个值:

a = [1,2,3]
b, *c = a

assert_equal [b, c], [1, [2,3]]

d, = a
assert_equal d, a[0]

Initialize multiple values from an array:

a = [1,2,3]
b, *c = a

assert_equal [b, c], [1, [2,3]]

d, = a
assert_equal d, a[0]
︶葆Ⅱㄣ 2024-10-12 05:30:29

我自己的方法是:

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

5.times.map { some_expression }

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

[value]*5

数组的元素求和:

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

查找与条件匹配的所有索引:

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

My own are:

Initialize array elements with same expression:

5.times.map { some_expression }

Initialize array with same value:

[value]*5

Sum elements of an array:

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

Find all indices that match condition:

a.each_with_index.find_all { |e, i| some_predicate(e) }.map(&:last)
清泪尽 2024-10-12 05:30:29

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

转换数组-> Hash(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]

Not really snippets, but I like these generic constructions (I show only how to use them, the implementation is easily found on the web).

Conversion Array -> Hash (to_hash or mash, the idea is the same, see Facets implementation):

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

Map + select/detect: You want to do a map and get only the first result (so a map { ... }.first would inefficient):

>> [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 iterations (lazy_map, lazy_select, ...). Example:

>> 1.upto(1e100).lazy_map { |x| 2 *x }.first(5)
=> [2, 4, 6, 8, 10]
诺曦 2024-10-12 05:30:29

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

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

count 表示您不必执行 i = 0i += 1 >。

next 意味着您可以完成该块的迭代并仍然提供答案,而不是一直等到最后。

(如果您愿意,可以将块的最后两行替换为单行 ! secondary_test?(item),但这会使它看起来更混乱)

Count the number of items that meet either one condition or another:

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

count means you don't have to do i = 0 and i += 1.

next means that you can finish that iteration of the block and still supply the answer, rather than hanging around until the end.

(If you wanted, you could replace the last two lines of the block with the single line ! second_test?(item), but that'd make it look messier)

提笔书几行 2024-10-12 05:30:29

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

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

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

"".methods.sort - [].methods

Exploring boolean member methods of a string:

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

Exploring string-specific methods:

"".methods.sort - [].methods
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文