Преглед и коментари над решенията.
def sum_two_series(first, second, index)
for i in 1 .. index - 1
first += second
first, second = second, first
end
first
end
def series(name, index)
if name == 'fibonacci' then sumtwo_series(1, 1, index) end
if name == 'lucas' then sumtwo_series(2, 1, index) end
if name=='summed' then sumtwo_series(1, 1, index)+sumtwo_series(2, 1,index) end
end
def series(type, n)
if type === 'fibonacci'
(0..n).inject([1, 0]){|(sum, item)| [item, sum + item]}[0]
elsif type === 'lucas'
(1..n).inject([-1, 2]){|(sum, item)| [item, sum + item]}[0]
else
series('fibonacci', n) + series('lucas', n)
end
end
def series(series_calculate , number)
fibonacci = (1..number).inject([0 , 1]) { |(a , b) , _| [b , a + b]}[0]
lucas = (1..number).inject([2 , 1]) { |(a , b) , _| [b , a + b] }[0]
if series_calculate == "fibonacci" then fibonacci
elsif series_calculate == "lucas" then lucas
elsif series_calculate == "sum" then fibonacci + lucas
end
end
def fibonacci(n)
return 1 if n == 1 or n == 2
return fibonacci(n-1) + fibonacci(n-2)
end
def lucas(n)
return 2 if n == 1
return 1 if n == 2
return lucas(n-1) + lucas(n-2)
end
def summed(n)
fibonacci(n) + lucas(n)
end
def series(name, n)
puts send(name, n)
end
def series(sequence, index)
case sequence
when 'fibonacci' then fibonacci(index)
when 'lucas' then lucas(index)
else fibonacci(index) + lucas(index)
end
end
def fibonacci(index)
case index
when 1, 2 then 1
else fibonacci(index - 1) + fibonacci(index - 2)
end
end
def lucas(index)
case index
when 1 then 2
when 2 then 1
else lucas(index - 1) + lucas(index - 2)
end
end
# http://mathforum.org/library/drmath/view/51448.html
PHI = (1 + Math.sqrt(5)) / 2
def fibonacci(n)
((PHI**n - (-PHI)**(-n)) / Math.sqrt(5)).round
end
def lucas(n)
(PHI**(n - 1) + (1 - PHI)**(n - 1)).round
end
def series(name, n)
case name
when "fibonacci" then fibonacci(n)
when "lucas" then lucas(n)
when "summed" then lucas(n) + fibonacci(n)
end
end
class SequenceMemberGenerator
def initialize(first_element, second_element)
@sequence_store = Hash.new do |sequence, index|
sequence[index] = sequence[index - 1] + sequence[index - 2]
end
@sequence_store[1] = first_element
@sequence_store[2] = second_element
end
def [](index)
@sequence_store[index]
end
end
class Fibonacci
def initialize
@@fibonacci_sequence ||= SequenceMemberGenerator.new(1, 1)
end
def [](index)
@@fibonacci_sequence[index]
end
end
class Lucas
def initialize
@@lucas_sequence ||= SequenceMemberGenerator.new(2, 1)
end
def [](index)
@@lucas_sequence[index]
end
end
class Summed
def [](index)
Fibonacci.new[index] + Lucas.new[index]
end
end
def series(name, index)
if name == 'fibonacci'
Fibonacci.new[index]
elsif name == 'lucas'
Lucas.new[index]
else
Summed.new[index]
end
end
insert
– мутиране
string.prepend('|').concat('|')
или интерполация: "|#{foo}|"
Дефинирането става с ключовата дума def
. Резултатът от функцията е
последният оценен израз, ако няма return
някъде по-рано.
def factorial(n)
if n == 1
1
else
factorial(n - 1) * n
end
end
В Ruby няма tail recursion оптимизация. Този код яде стек.
def
винаги дефинира метод в някакъв клас
def
не е в дефиниция на клас, отива като private
метод на Object
puts
е пример за нещо такова, както и методите, които дефинирате в irb
Object
е удачно само за кратки скриптове
Object
е ужасно лош стилЗа да добавите метод в съществуващ клас, например Array
, просто "отваряте" класа и дефинирате метода:
class Array
def fourty_second
self[41]
end
end
list = []
list[41] = 'The Universe'
list.fourty_second # "The Universe"
Можете да излезете от функция с return
:
def includes?(array, element)
array.each do |item|
return true if item == element
end
false
end
Разбира се, такава функция е излишна.
Може да ползвате array.include?(element)
.
Параметрите в Ruby могат да имат стойности по подразбиране:
def order(drink, size = 'large')
"A #{size} #{drink}, please!"
end
order 'tea' # "A large tea, please!"
order 'coffee', 'small' # "A small coffee, please!"
Методите в ruby могат да вземат променлив брой аргументи. Параметърът се означава със
*
и при извикване на функцията съдържа списък от аргументите.
def say_hi(name, *drinks)
"Hi, I am #{name} and I enjoy: #{drinks.join(', ')}"
end
say_hi 'Mityo', 'coffee', 'tea', 'water' # "Hi, I am Mityo and I enjoy: coffee, tea, water"
Параметърът за променлив брой аргументи може да е на всяка позиция в дефиницията:
def something(*a, b, c)
end
def something(a, *b, c)
end
Очевидно, може да има само един такъв параметър във функция.
Когато последният аргумент е хеш, може да изтървете фигурните скоби около него. Долните редове правят едно и също:
def order(drink, preferences)
end
order('Latte', {:size => 'grande', :syrup => 'hazelnut'})
order 'Latte', {:size => 'grande', :syrup => 'hazelnut'}
order 'Latte', :size => 'grande', :syrup => 'hazelnut'
order 'Latte', size: 'grande', syrup: 'hazelnut'
Така Ruby симулира извикване на функция с наименовани аргументи. Последният ред работи при версия 1.9+.
Често ще видите код в този вид:
def order(drink, preferences = {})
end
order 'Latte'
order 'Latte', size: 'grande', syrup: 'hazelnut'
Така preferences
е незадължителен и няма нужда да го подавате, ако
нямате предпочитания.
Този "трик" с хешовете се ползва много, понякога прекалено много. Той има и ред недостатъци:
preferences[:size]
)
preferences[:size] ||= 'grande'
preferences = {size: 'grande', syrup: 'hazelnut'}.merge(preferences)
Горните недостатъци и нуждата водят до появата на истински keyword arguments в Ruby 2.0.
def order(drink, size: 'grande', syrup: nil)
message = "You ordered a #{size} #{drink}"
message << " with a #{syrup} syrup" if syrup
message
end
order 'Latte' # "You ordered a grande Latte"
order 'Latte', syrup: 'hazelnut' # "You ordered a grande Latte with a hazelnut syrup"
order 'Latte', filling: 'chocolate' # error: ArgumentError
def order(drink:, size: 'grande', syrup: nil)
message = "You ordered a #{size} #{drink}"
message << " with a #{syrup} syrup" if syrup
message
end
order drink: 'Latte' # "You ordered a grande Latte"
order syrup: 'hazelnut', drink: 'Latte' # "You ordered a grande Latte with a hazelnut syrup"
order # error: ArgumentError: missing keyword: drink
def order(drink:, size: 'grande', **options)
message = "You ordered a #{size} #{drink}"
message << " with these options: #{options.inspect}" unless options.empty?
message
end
order drink: 'Latte'
# You ordered a grande Latte
order syrup: 'hazelnut', drink: 'Latte'
# You ordered a grande Latte with these options: {:syrup=>"hazelnut"}
order
# error: ArgumentError: missing keyword: drink
Името на метод може да завършва на ?
. Това се ползва за методи,
които връщат лъжа или истина (предикати):
def even?(n)
n % 2 == 0
end
even? 2
even? 3
Това е само конвенция.
Името на метод може да завършва на !
.
Това се ползва, когато методът има две версии с различно поведение:
numbers = [4, 1, 3, 2, 5, 0]
numbers.sort # връща нов списък
numbers.sort! # променя списъка на място
В случая, "по-опасният" метод завършва на удивителна.
Ако имате само една версия на метод с такова име, не слагайте удивителна.
Анонимни функции в Ruby се дефинират с lambda
. Имат три начина на извикване:
pow = lambda { |a, b| a**b }
pow.call 2, 3
pow[2, 3]
pow.(2, 3)
За нещастие, не може да извиквате така: double(2)
. Това е несъвместимо с
изтърваването на скобите при извикването на метод.
Може и така:
double = lambda do |x|
x * 2
end
Важи стандартната конвенция за { }
и do
/end
.
От 1.9 има по-симпатичен синтаксис за ламбди:
say_hi = lambda { puts 'Hi there!' }
double = lambda { |x| x * 2 }
divide = lambda { |a, b| a / b }
say_hi = -> { puts 'Hi there' }
double = ->(x) { x * 2 }
divide = -> a, b { a / b }
Всеки метод може да приеме допълнителен аргумент, който е "анонимна функция". Може да
го извикате от метода с yield
:
def twice
yield
yield
end
twice { puts 'Ruby rocks!' }
Блокът може да приема аргументи:
def sequence(first, last, step)
current = first
while current < last
yield current
current += step
end
end
sequence(1, 10, 2) { |n| puts n }
# Извежда 1, 3, 5, 7, 9
yield
се оценява до стойността на блока:
def calculate
result = yield(2)
"The result for 2 is #{result}"
end
calculate { |x| x**2 } # "The result for 2 is 4"
Или как можем да напишем filter
:
def filter(array)
result = []
array.each do |item|
result << item if yield item
end
result
end
filter([1, 2, 3, 4, 5]) { |n| n.odd? } # [1, 3, 5]
Разбира се, такъв метод в Ruby вече съществува – Enumerable#select
.
block_given?
ще ви каже дали методът е извикан с блок:
def i_can_haz_block
if block_given?
puts 'yes'
else
puts 'no'
end
end
i_can_haz_block # no
i_can_haz_block { 'something' } # yes
Ако имате ламбда, която искате да подадете като блок, може да ползвате &
:
is_odd = lambda { |n| n.odd? }
filter([1, 2, 3, 4, 5], &is_odd)
filter([1, 2, 3, 4, 5]) { |n| n.odd? }
Горните са (почти) еквиваленти. Има малка разлика в някои други случаи.
Ако искате да вземете блока като обект, има начин:
def invoke_with(*args, &block)
block.call *args
end
invoke_with(1, 2) { |a, b| puts a + b }
Тук виждате и как може да викате функция като използвате списък, вместо позиционни аргументи.
Може и така:
def make_block(&block)
block
end
doubler = make_block { |n| n * 2 }
doubler.call 2 # 4