20. Enumerator. Return от блокове. Още полезни библиотеки

20. Enumerator. Return от блокове. Още полезни библиотеки

20. Enumerator. Return от блокове. Още полезни библиотеки

4 януари 2017

Втори тест

напомняне

План до края на семестъра

Проекти

Enumerator-и

Някои методи на 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]

Enumerator-и

нещо като итератори

Енумераторите могат да се държат като итератори.

numbers = 1.upto(3)

numbers.next   # => 1
numbers.next   # => 2
numbers.next   # => 3
numbers.next   # => error: StopIteration

Kernel#loop

loop прави безкраен цикъл. Спира на StopIteration.

numbers = 1.upto(3)

loop do
  puts numbers.next
end

#=> 1
#=> 2
#=> 3

Enumerable и Enumerator

Enumerators 101

примери

enum = [1, 2].each # => #<Enumerator: [1, 2]:each>

enum.next # => 1
enum.next # => 2
enum.next # => error: StopIteration

Enumerators 102

примери

enum = Enumerator.new do |yielder|
  yielder << 1
  yielder << 2
end

enum.next # => 1
enum.next # => 2
enum.next # => error: StopIteration

Object#enum_for

Може да извадите енумератор от произволен метод с 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]

Object#enum_for

`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]

Object#to_enum

примери

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]

Enumerable и Enumerator (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>

#with_object и #with_index

Енумераторите имат някои интересни методи.

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_with_index

навръзване на енумератори

Ако ви се е случвало да ви трябва индекс в map:

words = %w( foo bar baz ).map.with_index do |word, index|
  "#{index}: #{word.upcase}"
end

words # => ["0: FOO", "1: BAR", "2: BAZ"]

Мързеливи енумератори

Примери

безкрайни поредици

(1..Float::INFINITY).map { |i| i * i }.first(5) # => ?
(1..Float::INFINITY).lazy.map { |i| i * i }.first(5) # => [1, 4, 9, 16, 25]

Примери

еднократно оценяване на целия chain

[
  ' a ',
  ' b ',
  ' c ',
].lazy.map { |x| p x.strip }.map { |x| p x.upcase }.take(1).to_a

Горното ще изведе на екрана:

"a"
"A" 

И ще върне списъка ["A"].

LazyEnumerator methods

Методи имплементирани в Enumerator::Lazy (документация) към момента:

Методи, които "материализират" lazy колекцията:

Кога ни е полезно?

Първите 10 четни числа на Фибоначи

# Решение 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

Генератор на Fibonacci

Кодът по-долу няма да приключи никога:

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 }

Генератор на Fibonacci с енумератори

Кодът по-долу ще работи:

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]

Въпроси за Enumerator?

Задайте ги сега

The Ten Commandments of Egoless Programming

The Ten Commandments of Egoless Programming

извадка

Class#new и Object#initialize

Всъщност, #initialize е просто instance метод.

Class#new е имплементиран горе-долу така:

class Class
  def new
    object = self.allocate
    object.send :initialize
    object
  end
end

Object#dup, Object#clone, #initialize_copy

retry в ensure

retry изпълнява begin блока отначало.

retries_left = 3

begin
  connect_to_facebook
rescue ConnectionError
  retries_left -= 1
  retry if retries_left > 0
end

next, break, redo, retry

next, break, redo, retry

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"

next, break, redo, retry

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"

Документация в Ruby

RDoc

YARD

Други - Rails API документацията

Други - API Dock

RDoc

Подробно относно RDoc

RubyDoc

Rails

rubyonrails.org

RuboCop

https://github.com/bbatsov/rubocop

Sidekiq

https://github.com/mperham/sidekiq

ActiveSupport

https://github.com/rails/rails/tree/master/activesupport

ActiveRecord

https://github.com/rails/rails/tree/master/activerecord

ActiveRecord-Sinatra

https://github.com/janko-m/sinatra-activerecord

Curses

command-line UIs

https://github.com/ruby/curses

CarrierWave и Paperclip

https://github.com/carrierwaveuploader/carrierwave

https://github.com/thoughtbot/paperclip

Kaminari и will_paginate

https://github.com/amatsuda/kaminari

https://github.com/mislav/will_paginate

MiniMagick

https://github.com/minimagick/minimagick

CanCanCan и Pundit

https://github.com/CanCanCommunity/cancancan

https://github.com/elabs/pundit

Devise

https://github.com/plataformatec/devise

Bcrypt-Ruby

https://github.com/codahale/bcrypt-ruby

ActiveAdmin

https://github.com/gregbell/active_admin

Nokogiri

https://github.com/sparklemotion/nokogiri

SASS

https://github.com/nex3/sass

SimpleForm и Formtastic

https://github.com/plataformatec/simple_form

https://github.com/justinfrench/formtastic

Capistrano

https://github.com/capistrano/capistrano

Passenger, Unicorn, Thin, Puma

https://github.com/phusion/passenger

Fog

https://github.com/fog/fog

Въпроси