10. Call stack. Изключения, част 2. Структура на skeptic

10. Call stack. Изключения, част 2. Структура на skeptic

10. Call stack. Изключения, част 2. Структура на skeptic

10 ноември 2014

Днес

Сбирки след лекцията в сряда

План за неделя, 16 ноември

традиционната планина

Новини за работни позиции с Ruby

Помощници за седмични учебни групи

Ориентировъчен план за следващите лекции

Въпрос 1

Кой е родителският клас на класа StandardError?

Exception.

Въпрос 2

Кой клас стои най-горе в йерархията на всички изключения (като не броим Object)?

Exception.

Въпрос 3

От какъв тип са изключенията, хвърлени така: raise 'foo'?

RuntimeError.

Въпрос 4

Как ще се оцени следният израз?

begin
  raise 'oh noes!'
rescue Exception
  'A general exception has occurred.'
rescue RuntimeError
  'A standard error has occurred.'
end

На низа "A general exception has occurred.". Изключението се хваща от първия rescue, който споменава клас exception_class, за който е вярно, че exception.is_a?(exception_class).

Call stack

Достъп до call стека

puts с много аргументи

лирическо отклонение

Пример за call stack

# inception.rb:
def roll_the_ball()         go_deep               end
def go_deep()               we_need_to_go_deeper  end
def we_need_to_go_deeper()  even_deeper_than_that end
def even_deeper_than_that() puts(caller)          end

roll_the_ball

Изпълняваме го с ruby inception.rb.

Пример за call stack - резултат

Примерът от предния слайд ще продуцира:

inception.rb:3:in `we_need_to_go_deeper'
inception.rb:2:in `go_deep'
inception.rb:1:in `roll_the_ball'
inception.rb:6:in `<main>' 

Call стекът обикновено е по-дълбок

(съкратен) пример от irb

> puts caller
.../irb/workspace.rb:86:in `eval'
.../irb/workspace.rb:86:in `evaluate'
.../irb/context.rb:380:in `evaluate'
.../irb.rb:492:in `block (2 levels) in eval_input'
.../irb.rb:624:in `signal_status'
.../irb.rb:489:in `block in eval_input'
.../irb/ruby-lex.rb:247:in `block (2 levels) in each_top_level_statement'
.../irb/ruby-lex.rb:233:in `loop'
.../irb/ruby-lex.rb:233:in `block in each_top_level_statement'
.../irb/ruby-lex.rb:232:in `catch'
.../irb/ruby-lex.rb:232:in `each_top_level_statement'
.../irb.rb:488:in `eval_input'
.../irb.rb:397:in `block in start'
.../irb.rb:396:in `catch'
.../irb.rb:396:in `start'
.../bin/irb:11:in `
' => nil

Изключения като цяло

Основни атрибути

Изключенията в Ruby са обекти като всичко останало – инстанции на клас Exception или негов наследник.

Имат три основни атрибута:

Основни атрибути (2)

Нека имаме инстанция на изключение в променлива error. Тогава:

Някои вградени изключения

foo               # NameError: undefined local variable or method `foo' for main:Object
1 / 0             # ZeroDivisionError: divided by 1
File.open         # ArgumentError: wrong number of arguments (0 for 1..3)
File.open('/Ah?') # Errno::ENOENT: No such file or directory @ rb_sysopen - /Ah?

Между другото, Errno::ENOENT си е нормално изключение:

Errno::ENOENT.ancestors.take_while { |kind| kind != Object }
# => [Errno::ENOENT, SystemCallError, StandardError, Exception]

Наши собствени изключения

За да си направим клас-изключение, обикновено наследяваме от RuntimeError или StandardError:

class NoFriendsError < StandardError
end

Как да ползваме изключения

Може да разделим изключенията на два основни вида.

  1. Непредвидими грешки, причинени от "околната среда".
  2. Програмистки грешки, причинени от неправилна употреба на парче код.

Непредвидими грешки

Програмистки грешки

Какво да хващаме?

Изключения в библиотеки

It is recommended that a library should have one subclass of StandardError or RuntimeError and have specific exception types inherit from it. This allows the user to rescue a generic exception type to catch all exceptions the library may raise even if future versions of the library add new exception subclasses.

Изключения в библиотеки (2)

catch и throw

catch и throw

def iterate_pairs(hash)
  hash.values.each { |array| iterate_values array }
end

def iterate_values(array)
  array.each do |item|
    if item == 'Nemo'
      puts 'Found Nemo!'
      throw :done
    end
  end
end

animals = {cats: %w[Simba], fish: %w[Crispy Nemo], boars: %w[Pumba]}
catch(:done) { iterate_pairs(animals) }

Този пример е доста синтетичен.

catch и throw

накратко

Всички възможни аргументи на метод

лирическо отклонение с елементи на преговор

def an_example_of_great_method_design(
  a,
  _,
  _,
  b,
  c = :something,
  *splat,
  d,
  (e, f),
  keyword:,
  with_default: 'value',
  **other_keyword_args,
  &some_block
)
end

Всички възможни аргументи на метод (2)

Sandi Metz

Като споменахме Санди...

I'm the author of Practical Object-Oriented Design in Ruby (POODR). I believe in simple code and straightforward explanations. I want to help you transform your code and make the pain go away.

Code spelunking: skeptic

Гмуркане в дълбините на skeptic, за да разберем как работи и как да правим това с други библиотеки.

Четвърто предизвикателство

тъжната действителност

Искахме от вас нещо просто:

remove_duplicates [-1, 4, -1, 33, 33, 42, 4] # [-1, 4, 33, 42]

Четвърто предизвикателство - добри решения (1)

Едно от възможните добри решения:

def remove_duplicates(integers)
  integers.each_with_object([]) do |integer, uniques|
    uniques << integer unless uniques.include? integer
  end
end

Четвърто предизвикателство - добри решения (2)

Още един вариант:

def remove_duplicates(integers)
  integers & integers
end

Някои вариации на горното:

def remove_duplicates(integers) integers | integers end
def remove_duplicates(integers) [] | integers end

Четвърто предизвикателство - добри решения (3)

Частично приемливо:

def remove_duplicates(integers)
  integers.to_set.to_a
end

Проблеми с това решение:

Четвърто предизвикателство - добри решения (4)

Както и това:

def remove_duplicates(integers)
  integers.group_by { |integer| integer }.keys
end

Четвърто предизвикателство - добри решения (5)

Приемливо, защото е прост, праволинеен и очакван начин за решение на проблема:

def remove_duplicates(integers)
  uniques = []
  integers.each { |integer| uniques << integer unless uniques.include?(integer) }
  uniques
end

Четвърто предизвикателство - проблеми

Четвърто предизвикателство - проблеми

Най-общо:

Въпроси