5

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
4

6 に答える 6

2

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))

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

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

ここでは、2 要素の代わりにHash#eachyieldを使用して実験しました。このような残忍なモンキー パッチを実行した後でも、どれだけ多くのコードがまだ機能しているかは、非常に驚​​くべきことです。おい、ダックタイピング!KeyValuePairArray

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

私が最近いじったもう 1 つのことは、 のすべてのメソッドを再実装することでしたが、代わりにをベースとしてEnumerable使用しました。この場合、実際には正しく機能しないことがわかっています。reduceeach

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
于 2010-11-30T19:54:14.877 に答える
1

配列から複数の値を初期化します。

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

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

d, = a
assert_equal d, a[0]
于 2011-03-07T21:14:34.103 に答える
0

私自身は:

同じ式で配列要素を初期化します。

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)
于 2010-11-30T19:19:51.013 に答える
0

文字列のブール メンバー メソッドの探索:

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

文字列固有のメソッドの探索:

"".methods.sort - [].methods
于 2011-03-07T21:11:39.347 に答える
0

スニペットではありませんが、これらの一般的な構造が気に入っています (使用方法のみを示します。実装は Web 上で簡単に見つけることができます)。

変換配列 -> ハッシュ (to_hashまたはmash、考え方は同じです。ファセットの実装を参照してください):

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

マップ + 選択/検出: マップを実行して、最初の結果のみを取得する必要があります (したがって、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]
于 2010-11-30T19:58:10.377 に答える
0

いずれかの条件を満たす項目の数を数えます。

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

countする必要がないことを意味しi = 0ますi += 1

nextつまり、最後までぶらぶらするのではなく、ブロックの反復を終了しても答えを提供できるということです。

(必要に応じて、ブロックの最後の 2 行を 1 行に置き換えることもできますが、! second_test?(item)それでは見栄えが悪くなります)

于 2010-11-30T22:47:15.950 に答える