Някои методи на Enumerable
могат да не вземат блок.
numbers = []
1.upto(5) { |x| numbers << x }
numbers # => [1, 2, 3, 4, 5]
other = 1.upto(5)
other # => #<Enumerator: 1:upto(5)>
other.to_a # => [1, 2, 3, 4, 5]
1.upto(5).map(&:succ) # => [2, 3, 4, 5, 6]
Енумераторите могат да се държат като итератори.
numbers = 1.upto(3)
numbers.next # => 1
numbers.next # => 2
numbers.next # => 3
numbers.next # => error: StopIteration
loop
прави безкраен цикъл. Спира на StopIteration
.
numbers = 1.upto(3)
loop do
puts numbers.next
end
#=> 1
#=> 2
#=> 3
Enumerable
връщат Enumerator
, ако ги извикате без блок
Enumerable
, помислете и за съвместимост с enumerator-иenum = [1, 2].each # => #<Enumerator: [1, 2]:each>
enum.next # => 1
enum.next # => 2
enum.next # => error: StopIteration
enum = Enumerator.new do |yielder|
yielder << 1
yielder << 2
end
enum.next # => 1
enum.next # => 2
enum.next # => error: StopIteration
Може да извадите енумератор от произволен метод с enum_for
.
class Numbers
def primes
yield 2
yield 3
yield 5
yield 7
end
end
first_four_primes = Numbers.new.enum_for(:primes)
first_four_primes.to_a # => [2, 3, 5, 7]
`enum_for` предава аргументите си на итериращия метод.
class Numbers
def primes(offset)
yield 2 + offset
yield 3 + offset
yield 5 + offset
yield 7 + offset
end
end
first_four_primes = Numbers.new.enum_for(:primes, 10)
first_four_primes.to_a # => [12, 13, 15, 17]
o = Object.new
def o.each
yield
yield 'hello'
yield [1, 2]
end
enum = o.to_enum
enum.next # => nil
enum.next # => "hello"
enum.next # => [1, 2]
class Foo
def each
return to_enum unless block_given?
yield 1
yield 2
end
end
f = Foo.new
f.each { |x| puts x } # => nil
f.each # => #<Enumerator: #<Foo:0x002b12935aa4a8>:each>
Енумераторите имат някои интересни методи.
numbers = 1.upto(3)
numbers.with_index.to_a # => [[1, 0], [2, 1], [3, 2]]
numbers.with_object(:x).to_a # => [[1, :x], [2, :x], [3, :x]]
Ако ви се е случвало да ви трябва индекс в map
:
words = %w( foo bar baz ).map.with_index do |word, index|
"#{index}: #{word.upcase}"
end
words # => ["0: FOO", "1: BAR", "2: BAZ"]
Enumerable#lazy
връща "мързелив" енумератор
Enumerator#lazy
(1..Float::INFINITY).map { |i| i * i }.first(5) # => ?
(1..Float::INFINITY).lazy.map { |i| i * i }.first(5) # => [1, 4, 9, 16, 25]
[
' a ',
' b ',
' c ',
].lazy.map { |x| p x.strip }.map { |x| p x.upcase }.take(1).to_a
Горното ще изведе на екрана:
"a" "A"
И ще върне списъка ["A"]
.
Методи имплементирани в Enumerator::Lazy (документация) към момента:
map
/ collect
flat_map
/ collect_concat
select
/ find_all
reject
grep
zip
take
и take_while
drop
и drop_while
slice_before
, slice_after
и slice_when
(документация)Методи, които "материализират" lazy колекцията:
to_a
/ force
each
next
# Решение 1
Fib.lazy.select(&:even?).take(10).to_a
# Решение 2
a = []
Fib.each do |x|
next if x.odd?
a << x
break if a.size == 10
end
Кодът по-долу няма да приключи никога:
class FibonacciNumbers
def each
current, previous = 1, 0
while true
yield current
current, previous = current + previous, current
end
end
end
FibonacciNumbers.new.each { |number| puts number }
Кодът по-долу ще работи:
class FibonacciNumbers
def each
current, previous = 1, 0
while true
yield current
current, previous = current + previous, current
end
end
end
FibonacciNumbers.new.to_enum.take(5) # => [1, 1, 2, 3, 5]
Задайте ги сега
Всъщност, #initialize
е просто instance метод.
Class#new
е имплементиран горе-долу така:
class Class
def new
object = self.allocate
object.send :initialize
object
end
end
#dup
и #clone
правят копие на обект
#initialize_copy
#clone
копира singleton методи и freeze-ва обекта, ако е замразенretry
изпълнява begin
блока отначало.
retries_left = 3
begin
connect_to_facebook
rescue ConnectionError
retries_left -= 1
retry if retries_left > 0
end
retry
може да се ползва в rescue
/ensure
клауза в метод
redo
– просто рестартира блока
next
– прекратява работата на блока
break
– прекратява работата на метода, извикващ блока
next
, redo
и break
могат да се използват само в блоковеnext
прекратява работата на блока:
def bar
p "bar started"
p yield
p yield
p "bar finished"
end
bar do
p "block started"
next "Return value"
p "block finished"
end
Резултатът ще е:
"bar started"
"block started"
"Return value"
"block started"
"Return value"
"bar finished"
=> "bar finished"
break
прекратява работата на блока и на метода, който го извиква:
def bar
p "bar started"
p yield
p yield
p "bar finished"
end
bar do
p "block started"
break "Return value"
p "block finished"
end
Резултатът ще е:
"bar started"
"block started"
=> "Return value"
https://github.com/rails/rails/tree/master/activesupport
https://github.com/rails/rails/tree/master/activerecord
mysql2
, pg
, sqlite3
и т.н.
https://github.com/ruby/curses