=
и особености около него
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
Анонимни функции в 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
->(**opts) { p [opts] }.call foo: 'bar', larodi: 'baz'
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.(*args)
end
invoke_with(1, 2) { |a, b| puts a + b }
Може и така:
def make_block(&block)
block
end
doubler = make_block { |n| n * 2 }
doubler.(2) # 4
В Ruby има два вида анонимни функции. Другият е Proc.
double = Proc.new { |x| x * 2 }
double.call(2)
double[2]
double.(2)
Дотук е същото, но има разлики при извикване.
f = | Proc.new { |x, y| p x, y } | lambda { |x, y| p x, y } |
---|---|---|
f.call(1) | 1 nil | ArgumentError |
f.call(1, 2) | 1 2 | 1 2 |
f.call(1, 2, 3) | 1 2 | ArgumentError |
f.call([1, 2]) | 1 2 | ArgumentError |
f.call(*[1, 2]) | 1 2 | 1 2 |
yield
ползва семантиката на Proc.new
lambda
Ако първият аргумент на функция е хеш, трябва да изтървете скобите на хеша, ако изтървете скобите на метода.
# Валиден код
order drink: 'latte', size: 'grande'
order({drink: 'latte', size: 'grande'})
# Невалиден код
order {drink: 'latte', size: 'grande'}
Във втория случай, Ruby си мисли, че му подавате блок.
Стандартните функционални неща:
numbers = [-9, -4, -1, 0, 1, 4, 9]
positive = numbers.select { |n| n >= 0 }
even = numbers.reject { |n| n.odd? }
squares = numbers.collect { |n| n ** 2 }
roots = numbers.select { |n| n > 0 }.collect { |n| n ** 0.5 }
#select
и #collect
имат синоними
#find_all
и #map
:
numbers = [-9, -4, -1, 0, 1, 4, 9]
squares = numbers.map { |n| n ** 2 }
positive = numbers.find_all { |n| n >= 0 }
В Ruby подобни синоними се срещат често.
#reduce
свежда списък до единична стойност с някаква операция:
numbers = [1, 2, 3, 4, 5]
numbers.reduce(0) { |a, b| a + b }
numbers.reduce(1) { |a, b| a * b }
numbers.reduce { |a, b| a + b }
numbers.reduce { |a, b| "#{a}, #{b}" }
#reduce
и #inject
са синоними. Ползвайте първото.
Имаме списък с думи. Искаме да получим хеш от вида {дума => дължина на думата}
:
words = %w[chunky bacon is awesome]
words.reduce({}) { |hash, word| hash[word] = word.length; hash }
Това е неидиоматично използване на #reduce
, но е интересен пример.
def reduce(array, initial = nil)
remaining = array.dup
buffer = initial || remaining.shift
until remaining.empty?
buffer = yield buffer, remaining.shift
end
buffer
end
reduce([1, 2, 3, 4]) { |a, b| a + b }
reduce([1, 2, 3, 4], 0) { |a, b| a + b }
За забавлението. Неяснотите — следващия път.
class Array
def reduce(initial = nil)
remaining = dup
buffer = initial || remaining.shift
until remaining.empty?
buffer = yield buffer, remaining.shift
end
buffer
end
end
answer = 42
a, b = 1, 2
a # 1
b # 2
a, b = b, a
a # 2
b # 1
Има няколко различни случая, които ще разгледаме.
a = 1, 2, 3
a # [1, 2, 3]
Практически същото като a = [1, 2, 3]
a, b = [1, 2, 3]
a # 1
b # 2
a, b = 1, 2, 3
a # 1
b # 2
nil
head, *tail = [1, 2, 3]
head # 1
tail # [2, 3]
first, *middle, last = 1, 2, 3, 4
first # 1
middle # [2, 3]
last # 4
middle
и tail
обират всичко останало
first, *middle, last = 1, [2, 3, 4]
first # 1
middle # []
last # [2, 3, 4]
first, *middle, last = 1, *[2, 3, 4]
first # 1
middle # [2, 3]
last # 4
head, (title, body) = [1, [2, 3]]
head # 1
title # 2
body # 3
head, (title, (body,)) = [1, [2, [3]]]
)
head, (title, *sentences) = 1, [2, 3, 4, 5, 6]
head # 1
title # 2
sentences # [3, 4, 5, 6]
Бележка за реда на оценка при присвояване — първо отдясно, след това отляво:
x = 0
a, b, c = x, (x += 1), (x += 1)
x # 2
a # 0
b # 1
c # 2
Може да ползвате едно име само един път, когато то се среща в списък с параметри на метод, блок и прочее.
Proc.new { |a, b, a| } # SyntaxError: duplicated argument name
Proc.new { |_, b, _| } # => #<Proc:0x007f818af68de0@(irb):23>
Горното важи не само за блокове, но и за методи.
success, message = execute(job)
[[1, [2, 3]], [4, [5, 6]], [7, [8, 9]]].each do |a, (b, c)|
puts "#{a}, #{b}, #{c}"
end
# 1, 2, 3
# 4, 5, 6
# 7, 8, 9
Имате ли въпроси по тази тема?
auto_link("Some text here.", options = {link_emails: false}, sanitize: true)