Мы рассмотрели некоторые из наиболее важных типов объектов Ruby в последних трех публикациях — строки, целые числа и числа с плавающей точкой, а также коллекции, такие как массивы, диапазоны и хеши. Мы также рассмотрели методы, которые придают этим объектам их функциональность. В этом посте мы рассмотрим создание наших собственных методов.
Чтобы определить метод, мы используем ключевое слово def
Далее следует код для метода перед завершением с ключевым словом end
Давайте посмотрим на некоторые примеры в IRB:
def say_hello
"Hello Ruby!"
end
Этот метод называется say_hello
Последняя строка метода в Ruby — это возвращаемое значение, которое является значением, которое возвращается методом при вызове. Чтобы вызвать метод, просто введите его имя:
say_hello
=> Hello Ruby!
Методы полезны. Они могут облегчить чтение вашего кода (если вы дадите описательные имена для ваших методов) и означают, что вам не нужно писать повторяющиеся блоки кода снова и снова. Кроме того, если вы хотите, чтобы некоторые функциональные возможности изменились, вам нужно обновить метод только в одном месте. Это известно как принцип СУХОЙ (не повторяй себя), и это важно помнить при программировании.
Добавление параметров
Методы можно сделать более эффективными, включив параметры . Это значения, которые предоставляют метод для использования. Например, мы можем улучшить метод say_hello
def say_hello(name)
"Hello #{name}!"
end
Параметры добавляются в определение метода после имени метода (круглые скобки необязательны, но рекомендуются). В теле функции имя параметра действует как переменная, равная значению, которое передается методу в качестве аргумента при его вызове:
say_hello "Sinatra" # Again, parentheses are optional
=> "Hello Sinatra!"
В этом случае строка «Sinatra» предоставляется в качестве аргумента, и мы используем интерполяцию строки, чтобы вставить аргумент в строку, возвращаемую методом.
Мы можем предоставить значение по умолчанию для параметра, поместив его равным значению по умолчанию в определении метода:
def say_hello(name="Ruby")
"Hello #{name}"
end
Это означает, что теперь мы можем вызвать метод say_hello
say_hello
=> "Hello Ruby!"
say_hello "DAZ"
=> "Hello DAZ!"
Мы можем добавить больше параметров, некоторые с параметрами по умолчанию, а некоторые без (но те, которые без, должны стоять на первом месте). Вот еще одна функция, называемая greet
def greet(name,job,greeting="Hello")
"#{greeting} #{name}, Your job as a #{job} sounds fun"
end
greet("Walt","Cook")
=> "Hello Walt, Your job as a Cook sounds fun"
greet("Jessie","Cook","Hey")
=> "Hey Jessie, Your job as a Cook sounds fun"
Мы также можем создавать методы с неопределенным числом аргументов, помещая ‘*’ перед последним параметром в определении метода. Любое количество аргументов будет затем сохранено в виде массива с тем же именем, что и параметр. Следующий метод примет любое количество имен в качестве аргумента и сохранит их в массиве с names
def say_hello(*names)
names.each { |name| puts "Hello #{name}!" }
end
say_hello("Walt","Skylar","Jessie")
Hello Walt!
Hello Skylar!
Hello Jessie!
=> ["Walt", "Sklar", "Jessie"]
say_hello "Sherlock", "John"
Hello Sherlock!
Hello John!
=> ["Sherlock", "John"]
say_hello "Heisenberg"
Hello Heisenberg!
=> ["Heisenberg"]
Обратите внимание, что возвращаемое значение метода — массив names
Другой вариант — вместо этого использовать ключевые аргументы (хотя они доступны только в Ruby версии 2.0 и выше). Они действуют с использованием синтаксиса, похожего на хеш, для параметров, как показано в обновленной версии нашего метода greet
def greet(name="Ruby", job: "programming language", greeting: "Hello")
"#{greeting} #{name}, Your job as a #{job} sounds fun"
end
Затем аргументы можно вводить в любом порядке, используя ключевые слова:
greet greeting: "Hi"
=> "Hi Ruby, Your job as a programming language sounds fun"
greet "Sherlock", job: "detective", greeting: "Greetings"
=> "Greetings Sherlock, Your job as a detective sounds fun"
greet "John", greeting: "Good Day", job: "doctor"
=> "Good Day John, Your job as a doctor sounds fun"
Обратите внимание, что порядок аргументов ключевого слова не имеет значения, и, если некоторые из них пропущены, вместо них используется значение по умолчанию.
Мы также можем добавить дополнительный параметр в конце с **
Это будет собирать любые дополнительные ключевые аргументы, которые не указаны в определении метода в хэше с тем же именем, что и параметр:
def greet(name="Ruby", job: "programming language", greeting: "Hello", **options)
"#{greeting} #{name}, Your job as a #{job} sounds fun. Here is some extra information about you: #{options}"
end
greet "Saul", job: "Colonel", human: false
=> "Hello Saul, Your job as a Colonel sounds fun. Here is some extra information about you: {:human=>false}"
greet "Kara", job: "Viper Pilot", callsign: "Starbuck", human: true
=> "Hello Kara, Your job as a Viper Pilot sounds fun. Here is some extra information about you: {:callsign=>\"Starbuck\", :human=>true}"
Также возможно добавить блок в качестве параметра в метод, поместив символ &
Затем к блоку можно обратиться в определении метода, обратившись к нему. Это полезно, если вы хотите запустить какой-то определенный код при вызове метода. Вот базовый пример метода с именем repeat
def repeat(number=2, &block)
number.times { block.call }
end
repeat(3) { puts "Ruby!" }
Ruby!
Ruby!
Ruby!
=> 3
Блок является необязательным, и есть удобный метод с именем block_given?
это позволяет нам проверить, задан ли блок при вызове метода. Вот метод roll_dice
def roll_dice(sides=6,&block)
if block_given?
block.call(rand(1..sides))
else
rand(1..sides)
end
end
Если метод вызывается без блока, он просто возвращает число, брошенное на кости:
roll_dice
=> 6
Если мы хотим имитировать 20-стороннюю кость, мы можем ввести аргумент sides
roll_dice(20)
=> 13
Теперь скажем, что мы хотим бросить кости, но затем удвоить результат и затем добавить 5. Мы можем использовать блок, чтобы сделать это:
roll_dice { |result| 2 * result + 5 }
Другой пример использования блока может быть, если мы хотим вернуть, был ли результат броска костей нечетным или четным:
roll_dice do |result|
if result.odd?
"odd"
else
"even"
end
end
Использование блоков в качестве параметров может сделать методы чрезвычайно гибкими и мощными. Обработчики маршрута в Синатре принимают блоки в качестве аргументов. Определение метода для запроса GET
def get(route,options={},&block)
... code goes here
end
Аргумент маршрута — это строка, которая сообщает нам маршрут для сопоставления. Затем следует хэш параметров, по умолчанию равный пустому хэшу. И наконец, метод принимает блок, который является кодом, который мы хотим запустить при посещении маршрута.
Рефакторинг Правильно разыграй свои карты
Мы собираемся провести рефакторинг кода для приложения Sinatra ‘Play Your Cards Right’, которое мы создали в последнем уроке. Рефакторинг кода — это процесс улучшения его структуры и удобства сопровождения без изменения его поведения.
То, что мы собираемся сделать, это заменить некоторые фрагменты кода методами. Это облегчит выполнение кода и его поддержку — если мы хотим внести изменения в функциональность, нам просто нужно изменить метод в одном месте.
Синатра использует вспомогательные методы для описания методов, которые используются в обработчиках маршрутов и представлениях. Они размещаются в блоке helpers
helpers do
# helper methods go here
end
Для начала, мы собираемся переписать приложение, используя имена методов для описания поведения, которое мы хотим. Создайте новый файл с именем ‘play_your_cards_right_refactored.rb’ и добавьте следующий код:
require 'sinatra'
enable :sessions
configure do
set :deck, []
suits = %w[ Hearts Diamonds Clubs Spades ]
values = %w[ Ace 2 3 4 5 6 7 8 9 10 Jack Queen King ]
suits.each do |suit|
values.each do |value|
settings.deck << "#{value} of #{suit}"
end
end
end
helpers do
# helper methods will go here
end
get '/' do
set_up_game
redirect to('/play')
end
get '/:guess' do
card = draw_card
value = value_of card
if player_has_a_losing value
game_over card
else
update_session_with value
ask_about card
end
end
Это очень похоже на последний кусок кода (и он имеет точно такую же функциональность), за исключением того, что гораздо проще увидеть, что происходит в каждом обработчике маршрута. Это потому, что мы заменили большую часть кода Ruby методами и выбрали некоторые описательные имена методов, чтобы сделать код более читабельным. Это почти похоже на псевдокод .
Давайте посмотрим на каждый обработчик маршрута по очереди, чтобы увидеть, что он делает:
get '/' do
set_up_game
redirect to('/play')
end
Этот обработчик маршрута настраивает игру, а затем перенаправляет на маршрут «/ play»… фактически, это даже не нужно объяснять, потому что он говорит это прямо в коде! Имена методов точно сообщают нам, что происходит — весь код был извлечен в метод с именем set_up_game
Поместите следующее внутри блока помощников:
def set_up_game
session[:deck] = settings.deck.shuffle
session[:guesses] = -1
session[:value] = 0
end
Это устанавливает переменные сеанса, которые нам понадобятся в игре. Колода перетасовывается, догадки устанавливаются на -1 (потому что она увеличивается на 1 при первой игре, даже если правильное предположение не было сделано), а переменная значения устанавливается на 0.
redirect
to
Теперь давайте посмотрим на начало обработчика маршрута, который имеет отношение к игровому процессу:
get '/:guess' do
card = draw_card
value = value_of card
# more code follows
end
Прежде всего нам нужно нарисовать карту, поэтому мы пишем метод для этого. Добавьте следующее в блок helpers
def draw_card
session[:deck].pop
end
Это очень короткий метод, но он дает нам более наглядный код.
Далее мы хотим узнать стоимость карты. Код, который мы использовали для этого в части 4, был довольно длинным case
Мы можем извлечь это в метод, который принимает в качестве аргумента карту, а затем возвращает значение этой карты. Следующий код также входит в блок helpers
def value_of card
case card[0]
when "J" then 11
when "Q" then 12
when "K" then 13
else card.to_i
end
end
Далее у нас есть оператор if
if player_has_a_losing value
game_over card
else
update_session_with value
ask_about card
end
Это также использует имя методов, чтобы сделать код очень описательным. Первый метод называется player_has_a_losing
value
Эта комбинация имени и имени параметра делает его хорошо читаемым. Этот метод также должен идти в блоке helpers
def player_has_a_losing value
(value < session[:value] and params[:guess] == 'higher') or (value > session[:value] and params[:guess] == 'lower')
end
Это возвращает true
false
session[:value]
params
:guess
Если метод player_has_a_losing
false
Первый — update_session_with
value
Это происходит точно так же, как указано, а также обновляет число догадок на 1. Это также входит в блок helpers
def update_session_with value
session[:value] = value
session[:guesses] += 1
end
Следующий метод, который должен идти в блоке helpers
ask_about
Это просто спрашивает игрока, является ли карта выше или ниже. Он принимает текущую карту в качестве аргумента:
def ask_about card
"The card is the #{ card }. Do you think the next card will be <a href='/higher'>Higher</a> or <a href='/lower'>Lower</a>?"
end
Это последний из всех наших вспомогательных методов. Если вы попытаетесь запустить код, введя ruby play_your_cards_right_refactored.rb
http: // localhost: 4567, вы должны увидеть ту же игру, что и раньше. Это именно то, что мы хотим сделать, когда мы реорганизуем наш код — никаких изменений снаружи, но более читаемый и поддерживаемый код внутри.
Сфера
В некоторых вспомогательных методах, которые мы только что использовали, мы должны были указать карту или значение в качестве параметра методов. Вы можете спросить, почему мы должны были это делать, когда card
value
Это связано с тем, что переменная существует внутри метода только в том случае, если она была создана в методе или если она была введена в качестве аргумента. Это можно увидеть в следующем примере (отметьте это в IRB):
name = "Walt"
job = "teacher"
def say_my_name
name = "Heisenberg"
job = "cook"
puts "You're #{name} and you're a #{job}"
end
puts "You're #{name} and you're a #{job}"
=> You're Walt and you're a teacher
say_my_name
=> You're Heisenberg and you're a Cook
Когда puts
name
job
name
job
У метода нет никакого доступа к любой переменной, созданной вне его (они должны быть введены как аргументы метода). Вы также не можете получить доступ к любой переменной, созданной внутри метода, вне метода — любые значения Вы хотите получить доступ после того, как метод был вызван, должен быть возвращен методом. Места в коде, где доступна переменная, известны как область действия переменной.
Это все люди
Надеемся, что это руководство помогло представить методы и показать, насколько они полезны для того, чтобы сделать ваш код более гибким, поддерживаемым, многократно используемым и более легким для чтения (при условии, что они хорошо названы).
Методы, которые мы писали в этом уроке, были больше похожи на функции . Как я упоминал в самом первом посте, Ruby на самом деле является объектно-ориентированным языком, а методы должны быть методами объектов. Методы, которые мы писали, на самом деле являются всеми методами специального main
Этот пост Pat для более подробной информации об этом).
В следующем посте мы станем классными и посмотрим, как работает система классов Ruby. Мы рассмотрим, как добавлять методы к существующим классам, таким как Strings и Integers, и как создавать свои собственные классы с их собственными открытыми и закрытыми методами. А пока, пожалуйста, оставляйте любые комментарии или вопросы, которые могут у вас возникнуть в разделе комментариев ниже.