Синтаксис Рубина
![]() | Эта статья написана как руководство или руководство . ( август 2021 г. ) |
В этой статье отсутствует информация о синтаксисе сопоставления с образцом в Ruby. ( август 2021 г. ) |
Синтаксис языка программирования Ruby во многом аналогичен синтаксису Perl и Python . Определения классов и методов обозначаются ключевыми словами, тогда как блоки кода могут определяться либо ключевыми словами, либо фигурными скобками. В отличие от Perl, переменные не обязательно имеют префикс сигилы . При использовании символ меняет семантику области действия переменной. Для практических целей нет различия между выражениями и утверждениями . [1] [2] Разрывы строк значимы и считаются концом оператора; эквивалентно может быть использована точка с запятой. В отличие от Python, отступы не имеют значения.
Одним из отличий от Python и Perl является то, что Ruby сохраняет все свои переменные экземпляра полностью закрытыми для класса и предоставляет их только через методы доступа ( attr_writer
, attr_reader
, и т. д.). В отличие от методов «getter» и «setter» других языков, таких как C++ или Java , методы доступа в Ruby могут быть созданы с помощью одной строки кода посредством метапрограммирования ; однако методы доступа также можно создавать традиционным способом C++ и Java. Поскольку вызов этих методов не требует использования круглых скобок, превратить переменную экземпляра в полную функцию очень просто, не изменяя ни одной строки вызывающего кода или не проводя рефакторинга, обеспечивая функциональность, аналогичную C# и VB.NET. членам свойств .
Дескрипторы свойств Python похожи, но в процессе разработки приходится идти на компромисс. Если кто-то начинает работать с Python с использования общедоступной переменной экземпляра, а затем изменяет реализацию, чтобы использовать частную переменную экземпляра, предоставляемую через дескриптор свойства, возможно, потребуется скорректировать внутренний код класса для использования частной переменной, а не публичного свойства. . Дизайн Ruby требует, чтобы все переменные экземпляра были закрытыми, но также предоставляет простой способ объявления set
и get
методы. Это соответствует идее, что в Ruby никогда не осуществляется прямой доступ к внутренним членам класса извне класса; скорее, каждый передает сообщение классу и получает ответ.
Интерактивные сессии
[ редактировать ]Следующие примеры можно запустить в оболочке Ruby, например Interactive Ruby Shell , или сохранить в файле и запустить из командной строки, набрав ruby <filename>
.
Классический Hello world пример :
puts 'Hello World!'
Некоторый базовый код Ruby:
# Everything, including a literal, is an object, so this works:
-199.abs # => 199
'ice is nice'.length # => 11
'ruby is cool.'.index('u') # => 1
"Nice Day Isn't It?".downcase.split('').uniq.sort.join
# => " '?acdeinsty"
Вход:
print 'Please type name >'
name = gets.chomp
puts "Hello #{name}."
Конверсии:
puts 'Give me a number'
number = gets.chomp
puts number.to_i
output_number = number.to_i + 1
puts output_number.to_s + ' is a bigger number.'
Струны
[ редактировать ]В Ruby существует множество способов определения строк.
Следующие задания эквивалентны:
a = "\nThis is a double-quoted string\n"
a = %Q{\nThis is a double-quoted string\n}
a = %{\nThis is a double-quoted string\n}
a = %/\nThis is a double-quoted string\n/
a = <<-BLOCK
This is a double-quoted string
BLOCK
Строки поддерживают интерполяцию переменных :
var = 3.14159
"pi is #{var}"
=> "pi is 3.14159"
Следующие присваивания эквивалентны и создают необработанные строки :
a = 'This is a single-quoted string'
a = %q{This is a single-quoted string}
Коллекции
[ редактировать ]Создание и использование массива :
a = [3, 'hello', 14.5, 1, 2, [6, 15]]
a[2] # => 14.5
a.[](2) # => 14.5
a.reverse # => [[6, 15], 2, 1, 14.5, 'hello', 3]
a.flatten.uniq # => [3, 'hello', 14.5, 1, 2, 6, 15]
Создание и использование ассоциативного массива (в Ruby называемого хешем ):
hash = Hash.new # equivalent to hash = {}
hash = { water: 'wet', fire: 'hot' } # makes the previous line redundant as we are now
# assigning hash to a new, separate hash object
puts hash[:fire] # prints "hot"
hash.each_pair do |key, value| # or: hash.each do |key, value|
puts "#{key} is #{value}"
end
# returns {:water=>"wet", :fire=>"hot"} and prints:
# water is wet
# fire is hot
hash.delete :water # deletes the pair :water => 'wet' and returns "wet"
hash.delete_if {|key,value| value == 'hot'} # deletes the pair :fire => 'hot' and returns {}
Структуры управления
[ редактировать ]Если заявление:
# Generate a random number and print whether it's even or odd.
if rand(100).even?
puts "It's even"
else
puts "It's odd"
end
Блоки и итераторы
[ редактировать ]Два синтаксиса для создания блока кода:
{ puts 'Hello, World!' } # note the braces
# or:
do
puts 'Hello, World!'
end
Блок кода можно передать методу в качестве необязательного аргумента блока. Многие встроенные методы имеют такие аргументы:
File.open('file.txt', 'w') do |file| # 'w' denotes "write mode"
file.puts 'Wrote some text.'
end # file is automatically closed here
File.readlines('file.txt').each do |line|
puts line
end
# => Wrote some text.
Передача параметров блока для замыкания :
# In an object instance variable (denoted with '@'), remember a block.
def remember(&a_block)
@block = a_block
end
# Invoke the preceding method, giving it a block that takes a name.
remember {|name| puts "Hello, #{name}!"}
# Call the closure (note that this happens not to close over any free variables):
@block.call('Jon') # => "Hello, Jon!"
Создание анонимной функции :
proc {|arg| puts arg}
Proc.new {|arg| puts arg}
lambda {|arg| puts arg}
->(arg) {puts arg} # introduced in Ruby 1.9
Возврат замыканий из метода:
def create_set_and_get(initial_value=0) # note the default value of 0
closure_value = initial_value
[ Proc.new {|x| closure_value = x}, Proc.new { closure_value } ]
end
setter, getter = create_set_and_get # returns two values
setter.call(21)
getter.call # => 21
# Parameter variables can also be used as a binding for the closure,
# so the preceding can be rewritten as:
def create_set_and_get(closure_value=0)
[ proc {|x| closure_value = x } , proc { closure_value } ]
end
Передача потока управления программой блоку, который был предоставлен во время вызова:
def use_hello
yield "hello"
end
# Invoke the preceding method, passing it a block.
use_hello {|string| puts string} # => 'hello'
Перебор перечислений и массивов с использованием блоков:
array = [1, 'hi', 3.14]
array.each {|item| puts item }
# prints:
# 1
# 'hi'
# 3.14
array.each_index {|index| puts "#{index}: #{array[index]}" }
# prints:
# 0: 1
# 1: 'hi'
# 2: 3.14
# The following uses a (a..b) Range
(3..6).each {|num| puts num }
# prints:
# 3
# 4
# 5
# 6
# The following uses a (a...b) Range
(3...6).each {|num| puts num }
# prints:
# 3
# 4
# 5
Такой метод, как inject
может принимать как параметр, так и блок. inject
метод перебирает каждый член списка, выполняя над ним некоторую функцию, сохраняя при этом агрегат. Это аналогично foldl
функции в функциональных языках программирования . Например:
[1,3,5].inject(10) {|sum, element| sum + element} # => 19
При первом проходе блок получает 10 (аргумент для инъекции) как sum
и 1 (первый элемент массива) как element
. Это возвращает 11, которое затем становится sum
на следующем проходе. Оно добавляется к 3, чтобы получить 14, которое затем добавляется к 5 на третьем проходе, чтобы в конечном итоге вернуть 19.
Использование перечисления и блока для возведения в квадрат чисел от 1 до 10 (с использованием диапазона ):
(1..10).collect {|x| x*x} # => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Или вызвать метод для каждого элемента ( map
является синонимом collect
):
(1..5).map(&:to_f) # => [1.0, 2.0, 3.0, 4.0, 5.0]
Классы
[ редактировать ]Следующий код определяет класс с именем Person
. В дополнение к initialize
, обычный конструктор для создания новых объектов, имеет два метода: один для переопределения <=>
оператор сравнения (так Array#sort
можно сортировать по возрасту), а другой — переопределить to_s
метод (так Kernel#puts
может форматировать вывод). Здесь, attr_reader
является примером метапрограммирования в Ruby: attr_accessor
определяет методы получения и установки переменных экземпляра, но attr_reader
только методы получения. Последний вычисленный оператор в методе является его возвращаемым значением, что позволяет опустить явный оператор. return
заявление.
class Person
attr_reader :name, :age
def initialize(name, age)
@name, @age = name, age
end
def <=>(person) # the comparison operator for sorting
@age <=> person.age
end
def to_s
"#{@name} (#{@age})"
end
end
group = [
Person.new("Bob", 33),
Person.new("Chris", 16),
Person.new("Ash", 23)
]
puts group.sort.reverse
Приведенный выше код выводит три имени в обратном порядке возраста:
Bob (33)
Ash (23)
Chris (16)
Person
является константой и является ссылкой на Class
объект.
Открытые занятия
[ редактировать ]В Ruby классы никогда не закрываются: методы всегда можно добавить в существующий класс. Это относится ко всем классам, включая стандартные встроенные классы. Все, что нужно сделать, — это открыть определение класса для существующего класса, и указанное новое содержимое будет добавлено к существующему содержимому. Простой пример добавления нового метода в стандартную библиотеку. Time
сорт:
# re-open Ruby's Time class
class Time
def yesterday
self - 86400
end
end
today = Time.now # => 2013-09-03 16:09:37 +0300
yesterday = today.yesterday # => 2013-09-02 16:09:37 +0300
Добавление методов к ранее определенным классам часто называется «обезьяньим исправлением» . Если выполнять эту практику безрассудно, эта практика может привести как к коллизиям поведения с последующими неожиданными результатами, так и к проблемам масштабируемости кода.
Начиная с Ruby 2.0 появилась возможность использовать усовершенствования , чтобы уменьшить потенциально негативные последствия обезьяньего исправления, ограничивая область действия исправления определенными областями базы кода.
# re-open Ruby's Time class
module RelativeTimeExtensions
refine Time do
def half_a_day_ago
self - 43200
end
end
end
module MyModule
class MyClass
# Allow the refinement to be used
using RelativeTimeExtensions
def window
Time.now.half_a_day_ago
end
end
end
Исключения
[ редактировать ]Исключение вызывается с помощью raise
вызов:
raise
К исключению можно добавить необязательное сообщение:
raise "This is a message"
Исключения также могут быть указаны программистом:
raise ArgumentError, "Illegal arguments!"
Альтернативно, экземпляр исключения может быть передан в метод raise
метод:
raise ArgumentError.new("Illegal arguments!")
Эта последняя конструкция полезна при создании экземпляра пользовательского класса исключений с конструктором, принимающим более одного аргумента:
class ParseError < Exception
def initialize(input, line, pos)
super "Could not parse '#{input}' at line #{line}, position #{pos}"
end
end
raise ParseError.new("Foo", 3, 9)
Исключения обрабатываются rescue
пункт. Такое предложение может перехватывать исключения, которые наследуются от StandardError
. Другие ключевые слова управления потоком, которые можно использовать при обработке исключений: else
и ensure
:
begin
# do something
rescue
# handle exception
else
# do this if no exception was raised
ensure
# do this whether or not an exception was raised
end
Распространенной ошибкой является попытка перехватить все исключения с помощью простого предложения спасения. Чтобы перехватить все исключения, нужно написать:
begin
# do something
rescue Exception
# Exception handling code here.
# Don't write only "rescue"; that only catches StandardError, a subclass of Exception.
end
Или поймайте определенные исключения:
begin
# do something
rescue RuntimeError
# handle only RuntimeError and its subclasses
end
Также можно указать, чтобы объект исключения был доступен для предложения обработчика:
begin
# do something
rescue RuntimeError => e
# handling, possibly involving e, such as "puts e.to_s"
end
Альтернативно, самое последнее исключение сохраняется в глобальном волшебном файле. $!
.
Также можно поймать несколько исключений:
begin
# do something
rescue RuntimeError, Timeout::Error => e
# handling, possibly involving e
end
Metaprogramming
[ редактировать ]Код Ruby может программно изменять во время выполнения аспекты своей собственной структуры, которые были бы зафиксированы в более жестких языках, такие как определения классов и методов. Этот вид метапрограммирования можно использовать для написания более лаконичного кода и эффективного расширения языка.
Например, следующий код Ruby генерирует новые методы для встроенного String
класс, основанный на списке цветов. Методы оборачивают содержимое строки HTML-тегом, оформленным соответствующим цветом.
COLORS = { black: "000",
red: "f00",
green: "0f0",
yellow: "ff0",
blue: "00f",
magenta: "f0f",
cyan: "0ff",
white: "fff" }
class String
COLORS.each do |color,code|
define_method "in_#{color}" do
"<span style=\"color: ##{code}\">#{self}</span>"
end
end
end
Сгенерированные методы затем можно было бы использовать следующим образом:
"Hello, World!".in_blue
=> "<span style=\"color: #00f\">Hello, World!</span>"
Чтобы реализовать эквивалент на многих других языках, программисту пришлось бы написать каждый метод ( in_black
, in_red
, in_green
и т. д.) отдельно.
Некоторые другие возможные варианты использования метапрограммирования Ruby включают в себя:
- перехват и изменение вызовов методов
- внедрение новых моделей наследования
- динамическое создание классов из параметров
- автоматическая сериализация объектов
- интерактивная помощь и отладка
Ссылки
[ редактировать ]- ^ «[ruby-talk:01120] Re: Значение while...»
В синтаксисе Ruby оператор — это всего лишь особый случай выражения, которое не может выступать в качестве аргумента (например, множественное присваивание).
- ^ "[ruby-talk:02460] Re: Вопрос о приоритете" .
оператор [...] не может быть частью выражения, если он не сгруппирован в круглые скобки.