Какво ще стане ако достъпим недефинирана инстанционна променлива?
Избройте поне две разлики в поведението на lambda
и proc
.
proc
обектите не са
return
в тяло на lambda
прекратява работата на lambda
-та, а в proc
– на обгръщащата функция
class
initialize
- нещо като конструктор
@
и се достъпват само отвътре
#foo
, #foo=
, attr_reader :foo
, attr_writer :foo
и attr_accessor :foo
Ако извикате #methods
на нещо, ще получите масив от символи
с имената на методите му.
Помните ли Array#-
?
class Person
def foo() end
def bar() end
end
Person.new.methods - Object.new.methods # => [:foo, :bar]
Много интуитивно.
class Vector
attr_accessor :x, :y
def initialize(x, y)
@x, @y = x, y
end
def +(other)
Vector.new(x + other.x, y + other.y)
end
def inspect
"Vector.new(#@x, #@y)"
end
end
Vector.new(1, 5) + Vector.new(3, 10) # => Vector.new(4, 15)
Ето и всички оператори, които можете да предефинирате:
+ - * / % **
^ | &
<= < > >=
<=> == === != =~ !~
[] []= >> <<
, както и унарните + -
a + b
всъщност се извиква a.+(b)
(методът + от класа на a
)
+= >>= &&=
не могат да се предефинират, но действат според очакваното.
a += b
е просто синтаксис за a = a + b
+ -
съответно чрез методите +@ -@
Сега е моментът да ги зададете :-)
class Person
def say_hi
"Hello! I am #{name}"
end
private
def name
'the Doctor'
end
end
person = Person.new
person.say_hi # => "Hello! I am the Doctor"
person.name # => error: NoMethodError
Ако един метод е private
, не може да го викате с явен получател.
Дори със self.
class Person
def say_hi
"Hello! I am #{self.name}"
end
private
def name
'the Doctor'
end
end
person = Person.new
person.say_hi # => error: NoMethodError
protected
private
protected
и private
отново в следващи лекцииСега е моментът да ги зададете :-)
Модулите в Ruby имат няколко предназначения:
Днес ще разгледаме последното.
Модулите в Ruby просто съдържат методи. Дефинират се подобно на класове:
module UselessStuff
def almost_pi
3.1415
end
def almost_e
2.71
end
end
Модулите могат да се "миксират" с клас. Тогава той получава всички методи на модула като инстанционни методи.
module UselessStuff
def almost_pi
3.1415
end
end
class Something
include UselessStuff
end
Something.new.almost_pi # => 3.1415
В метод на модула, self
е инстанцията от класа, в който модулът е бил миксиран и на която е извикан даденият метод.
module Introducable
def introduction
"Hello, I am #{name}"
end
end
class Person
include Introducable
def name() 'The Doctor' end
end
doctor = Person.new
doctor.introduction # => "Hello, I am The Doctor"
Методите на класа имат приоритет пред методите на модула.
module Includeable
def name() 'Module' end
end
class Something
def name() 'Class' end
include Includeable
end
Something.new.name # => "Class"
Ако два модула дефинират един и същи метод, ползва се методът от последно миксирания модул:
module Chunky
def name() 'chunky' end
end
module Bacon
def name() 'bacon' end
end
class Something
include Chunky
include Bacon
end
Something.new.name # => "bacon"
Просто за информация: методите на mixin-ите имат приоритет пред тези на родителя.
Всичко това е свързано с нещо, наречено ancestor chain, за което ще си говорим следващия път.
Сега е моментът да ги зададете :-)
Помните ли тези методи и техните синоними?
[1, 2, 3, 4, 5].select { |n| n.odd? } # => [1, 3, 5]
%w(foo plugh barney).map { |word| word.length } # => [3, 5, 6]
[1, 2, 3, 4, 5].reduce { |a, b| a * b } # => 120
Те са имплементирани в Enumerable
, а не в Array
.
Всяка колекция в Ruby ги има.
all? any? chunk collect collect_concat count cycle detect drop drop_while each_cons each_entry each_slice each_with_index each_with_object entries find find_all find_index first flat_map grep group_by include? inject lazy map max max_by member? min min_by minmax minmax_by none? one? partition reduce reject reverse_each select slice_before sort sort_by take take_while to_a zip
По-нататък ще видите как използваме Enumerable, за да генерираме тази таблица.
Хешовете също са Enumerable
:
hash = {2 => 3, 4 => 5}
hash.to_a # => [[2, 3], [4, 5]]
hash.map { |p| p[0] + p[1] } # => [5, 9]
hash.map { |k, v| k + v } # => [5, 9]
hash.reduce(0) { |s, p| s + p[0] * p[1] } # => 26
Някои от Enumerable
методите в Hash
са предефинирани.
hash = {2 => 3, 4 => 5, 6 => 7, 8 => 9}
hash.select { |k, v| v > 6 } # => {6=>7, 8=>9}
hash.to_a.select { |k, v| v > 6 } # => [[6, 7], [8, 9]]
Enumerable#select
връща списък, но Hash#select
връща хеш.
Hash
, Array
Range
от числа, дати, символи и прочее
Set
и други...include Enumerable
#each
class FibonacciNumbers
include Enumerable
def initialize(limit)
@limit = limit
end
def each
current, previous = 1, 0
while current < @limit
yield current
current, previous = current + previous, current
end
end
end
FibonacciNumbers.new(100).to_a # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
class StepRange
include Enumerable
def initialize(first, last, step)
@first, @last, @step = first, last, step
end
def each
@first.step(@last, @step) { |n| yield n }
end
end
StepRange.new(1, 10, 2).select { |n| n > 5 } # => [7, 9]
Сега е моментът да ги зададете :-)
#all?
/#any?
връщат истина, ако всички/един елемент(и) от
колекцията отговарят на някакво условие.
[1, 2, 3, 4].all? { |x| x.even? } # => false
[1, 2, 3, 4].any? { |x| x.even? } # => true
[2, 4, 6, 8].all? { |x| x.even? } # => true
[2, 4, 6, 8].any? { |x| x.odd? } # => false
# И разбира се:
[1, 2, 3, 4].any?(&:even?) # => true
#all?
и #any?
могат да работят и без блок:
[1, 2, 3, nil].all? # => false
[1, 2, 3, :nil].all? # => true
[1, 2, 3, false].any? # => true
Аналогични на #all?
и #any?
. Също могат да работят без блок.
%w(foo bar larodi).one? { |word| word.length == 6 } # => true
%w(foo bar larodi).one? { |word| word.length == 3 } # => false
[1, 5, 3].none? { |number| number.even? } # => true
[1, 2, 3].none? { |number| number.even? } # => false
[1, 2, 3].one? # => false
[1, nil, nil].one? # => true
class Integer
def prime?
return false if self == 1
2.upto(self ** 0.5).none? { |n| self % n == 0 }
end
end
7.prime? # => true
8.prime? # => false
#each_with_index
yield-ва всеки елемент с индекса му в масива:
%w[foo bar baz].each_with_index do |word, index|
puts "#{index}. #{word}"
end
Горното извежда:
0. foo 1. bar 2. baz
Няма #map_with_index
.
Затова пък има нещо по-общо:
%w[foo bar baz].map.with_index do |word, index|
[index, word]
end
# => [[0, "foo"], [1, "bar"], [2, "baz"]]
Името казва всичко, което ви е нужно да знаете.
words = %w(foo bar plugh larodi)
groups = words.group_by { |word| word.length }
groups # => {3=>["foo", "bar"], 5=>["plugh"], 6=>["larodi"]}
#each_slice(n)
yield
-ва елементите на части по n
:
%w(a b c d e f g h).each_slice(3) do |slice|
p slice
end
Извежда
["a", "b", "c"] ["d", "e", "f"] ["g", "h"]
#each_cons(n)
yield
"подмасиви" с n
елемента:
[1, 2, 3, 4, 5].each_cons(3) do |cons|
p cons
end
Извежда
[1, 2, 3] [2, 3, 4] [3, 4, 5]
Вече знаете какво прави:
[1, 2, 3, 4].include? 3 # => true
[1, 2, 3, 4].member? 5 # => false
Двете са синоними.
(0...1.0 / 0.0).include? -1 # => false
1 / 0 # => error: ZeroDivisionError
1.0 / 0.0 # => Infinity
[1, 2, 3].zip([4, 5, 6]) # => [[1, 4], [2, 5], [3, 6]]
[1, 2].zip([3, 4], [5, 6]) # => [[1, 3, 5], [2, 4, 6]]
[1, 2, 3, 4, 5].take(2) # => [1, 2]
[1, 2, 3, 4, 5].drop(2) # => [3, 4, 5]
[1, 3, 5, 6, 7, 9].take_while(&:odd?) # => [1, 3, 5]
[1, 3, 5, 6, 7, 9].drop_while(&:odd?) # => [6, 7, 9]
all? any? chunk collect collect_concat count cycle detect drop drop_while each_cons each_entry each_slice each_with_index each_with_object entries find find_all find_index first flat_map grep group_by include? inject lazy map max max_by member? min min_by minmax minmax_by none? one? partition reduce reject reverse_each select slice_before sort sort_by take take_while to_a zip
Enumerable.instance_methods.
sort.
map { |name| name.to_s.ljust(16) }.
each_slice(5) { |row| puts row.join '' }
Disclaimer: Леко е редактиран whitespace-а, за да се събере в слайд.
След това ще продължим с наследяване.
Наследяването в Ruby става така:
class Person
def name() 'The Doctor' end
end
class PolitePerson < Person
def introduction
"Hi, I am #{name}"
end
end
PolitePerson.new.introduction # => "Hi, I am The Doctor"
Object
Object
(с изключение на родителя на Object
...)Имате достъп до private
методите:
class Person
private
def name() 'The Doctor' end
end
class PolitePerson < Person
def introduction() "Hi, I am #{name}" end
end
PolitePerson.new.introduction # => "Hi, I am The Doctor"
class Base; end
class SpaceStation < Base; end
base = Base.new
station = SpaceStation.new
base.is_a? Base # => true
station.is_a? SpaceStation # => true
station.is_a? Base # => true
base.instance_of? Base # => true
station.instance_of? SpaceStation # => true
station.instance_of? Base # => false
#is_a?
има синоним #kind_of?
class Base; end
class SpaceStation < Base; end
base = Base.new
station = SpaceStation.new
base.kind_of? Base # => true
station.kind_of? SpaceStation # => true
station.kind_of? Base # => true
#is_a?
по принцип.
#instance_of?
само когато искате да не бъде наследник.
Може да предефинирате метод и да извикате версията на родителя със super
.
class Person
def introduction_to(other)
"Hello #{other}."
end
end
class PolitePerson < Person
def introduction_to(other)
super("Mr. #{other}") + " How do you do?"
end
end
queen = PolitePerson.new
queen.introduction_to('Smith') # => "Hello Mr. Smith. How do you do?"
Ако извикате super
без скоби, родителският метод получава същите аргументи.
class Person
def introduction_to(other)
"Hello #{other}."
end
end
class PolitePerson < Person
def introduction_to(other)
super + " How do you do?"
end
end
queen = PolitePerson.new
queen.introduction_to('Smith') # => "Hello Smith. How do you do?"
super
и super()
са различни:
class Person
def introduction_to(other)
"Hello #{other}."
end
end
class PolitePerson < Person
def introduction_to(other)
super() + " How do you do?"
end
end
queen = PolitePerson.new
queen.introduction_to('Smith') # => error: ArgumentError
include
-нати
ancestor chain
module Foo; end
module Bar; end
module Qux; end
class Base
include Foo
end
class Derived < Base
include Bar
include Qux
end
Derived.ancestors # => [Derived, Qux, Bar, Base, Foo, Object, Kernel, BasicObject]
module Foo; end
module Bar; end
module Qux
include Foo
include Bar
end
class Thing
include Qux
end
Thing.ancestors # => [Thing, Qux, Bar, Foo, Object, Kernel, BasicObject]
Има само една версия на метода:
module Talking
def greeting() "Hello, #{name}" end
end
class Base
include Talking
def name() 'Base' end
def say_hi_base() greeting end
end
class Derived < Base
include Talking
def name() 'Derived' end
def say_hi_derived() greeting end
end
derived = Derived.new
derived.say_hi_base # => "Hello, Derived"
derived.say_hi_derived # => "Hello, Derived"
Derived.ancestors # => [Derived, Base, Talking, Object, Kernel, BasicObject]