Офіційний FAQ Ruby
If you wish to report errors or suggest improvements for this FAQ, please go to our GitHub repository and open an issue or pull request.
Вбудовані бібліотеки
Що повертає instance_methods(false)?
Метод instance_methods повертає масив із назвами методів екземпляра
в класі або модулі-отримувачі. Це включає методи суперкласів і
домішаних модулів.
instance_methods(false) або instance_methods(nil) повертає назви
лише тих методів, що визначені в отримувачі.
Як працюють зерна генератора випадкових чисел?
Якщо rand викликається без попереднього виклику srand, генератор
псевдовипадкових чисел Ruby використовує довільне (майже випадкове)
зерно, яке, серед іншого, бере ентропію з джерела ОС, якщо воно доступне.
Послідовні запуски програми, що не використовує srand, генеруватимуть
різні послідовності випадкових чисел.
Для тестування можна отримати передбачувану поведінку з однаковою
послідовністю чисел при кожному запуску, викликавши srand із
фіксованим зерном.
Я прочитав файл і змінив його, але файл на диску не змінився.
File.open("example", "r+").readlines.each_with_index do |line, i|
line[0,0] = "#{i+1}: "
end
Ця програма не додає номери рядків до файла example. Вона читає
вміст файла і для кожного рядка додає номер на початку, але дані
ніколи не записуються назад. Наведений нижче код дійсно оновлює
файл (хоча це дещо небезпечно, бо перед оновленням не створюється
резервна копія):
File.open("example", "r+") do |f|
lines = f.readlines
lines.each_with_index {|line, i| line[0,0] = "#{i+1}: " }
f.rewind
f.puts lines
end
Як опрацювати файл і оновити його вміст?
Використовуючи параметр командного рядка -i або вбудовану змінну $-i,
можна читати файл і замінювати його.
Код із попереднього запитання, який додавав номери рядків до файла, найкраще записати цим способом:
$ ruby -i -ne 'print "#$.: #$_"' example
Якщо потрібно зберегти оригінальний файл, використайте -i.bak, щоб
створити резервну копію.
Я записав файл, скопіював його, але кінець копії ніби втрачено.
Цей код працюватиме неправильно:
require "fileutils"
File.open("file", "w").puts "This is a file."
FileUtils.cp("file", "newfile")
Оскільки введення/виведення буферизується, file копіюється до того, як
його вміст записано на диск. newfile, імовірно, буде порожнім. Однак
коли програма завершується, буфери скидаються, і file має очікуваний
вміст.
Проблеми не буде, якщо переконатися, що file закритий перед копіюванням:
require "fileutils"
File.open("file", "w") {|f| f.puts "This is a file." }
FileUtils.cp("file", "newfile")
Як отримати номер рядка в поточному вхідному файлі?
Під час читання з файла Ruby збільшує лічильник рядків у глобальній
змінній $.. Це також доступно через атрибут lineno об’єкта File.
Спеціальна константа ARGF — це об’єкт, схожий на файл, який можна
використовувати для читання всіх вхідних файлів, вказаних у командному
рядку (або стандартного вводу, якщо файлів немає). ARGF неявно
використовується в такому коді:
while gets
print $_
end
У цьому випадку $. буде кумулятивною кількістю рядків, прочитаних
у всіх вхідних файлах. Щоб отримати номер рядка в поточному файлі,
використайте
ARGF.file.lineno
Також можна отримати назву поточного файла через ARGF.file.path.
Як використовувати less для показу виводу програми?
Я спробував таке, але нічого не вивелося:
open("|less", "w").puts "abc"
Це тому, що програма завершується одразу, і less не встигає побачити те,
що ви йому записали, не кажучи вже про відображення. Переконайтеся, що
потік правильно закривається, і він чекатиме, доки завершиться less.
open("|less", "w") {|f| f.puts "abc" }
Що відбувається з об’єктом File, на який більше ніхто не посилається?
Об’єкт File, на який більше немає посилань, стає придатним для
збирання сміття. Файл буде закрито автоматично, коли об’єкт File буде
зібрано.
Мені не по собі, якщо я не закриваю файл.
Є принаймні чотири добрі способи гарантувати, що файл буде закрито:
# (1)
f = File.open("file")
begin
f.each {|line| print line }
ensure
f.close
end
# (2)
File.open("file") do |f|
f.each {|line| print line }
end
# (3)
File.foreach("file") {|line| print line }
# (4)
File.readlines("file").each {|line| print line }
Як відсортувати файли за часом зміни?
Dir.glob("*").sort {|a, b| File.mtime(b) <=> File.mtime(a) }
Хоча це працює (повертає список у зворотному хронологічному порядку), це не дуже ефективно, адже під час кожного порівняння отримує час модифікації файлів з ОС.
Більшої ефективності можна досягти ціною додаткової складності:
Dir.glob("*").map {|f| [File.mtime(f), f] }.
sort {|a, b| b[0] <=> a[0] }.map(&:last)
Як порахувати частоту слів у файлі?
freq = Hash.new(0)
File.read("example").scan(/\w+/) {|word| freq[word] += 1 }
freq.keys.sort.each {|word| puts "#{word}: #{freq[word]}" }
Виведе:
and: 1
is: 3
line: 3
one: 1
this: 3
three: 1
two: 1
Як відсортувати рядки в алфавітному порядку?
Якщо ви хочете, щоб рядки сортувалися як ‘AAA’, ‘BBB’, …, ‘ZZZ’, ‘aaa’, ‘bbb’, то вбудоване порівняння працюватиме чудово.
Якщо потрібно сортувати без урахування регістру, порівнюйте зведені до нижнього регістру рядки в блоці сортування:
array = %w( z bB Bb bb Aa BB aA AA aa a A )
array.sort {|a, b| a.downcase <=> b.downcase }
# => ["a", "A", "Aa", "aA", "AA", "aa", "bB", "Bb", "bb", "BB", "z"]
Якщо потрібно сортувати так, щоб ‘A’ і ‘a’ йшли поруч, але ‘a’ вважалася більшою за ‘A’ (тобто ‘Aa’ після ‘AA’, але перед ‘AB’), використайте:
array.sort {|a, b| (a.downcase <=> b.downcase).nonzero? || a <=> b }
# => ["A", "a", "AA", "Aa", "aA", "aa", "BB", "Bb", "bB", "bb", "z"]
Як перетворити табуляції на пробіли?
Якщо a містить рядок, який потрібно розгорнути, можна використати один із:
1 while a.sub!(/(^[^\t]*)\t(\t*)/){$1+" "*(8-$1.size%8+8*$2.size)}
# or
1 while a.sub!(/\t(\t*)/){" "*(8-$~.begin(0)%8+8*$1.size)}
# or
a.gsub!(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")}
Як екранувати зворотний слеш у регулярному виразі?
Regexp.quote('\\') екранує зворотний слеш.
Складніше, якщо ви використовуєте sub і gsub. Скажімо, ви пишете
gsub(/\\/, '\\\\'), сподіваючись замінити кожен зворотний слеш двома.
Другий аргумент перетворюється на '\\' під час синтаксичного
аналізу. Коли відбувається підстановка, механізм регулярних виразів
перетворює це на '\', тож у підсумку кожен окремий зворотний слеш
замінюється іншим окремим зворотним слешем. Треба писати
gsub(/\\/, '\\\\\\')!
Втім, з урахуванням того, що \& містить збіг, можна також написати
gsub(/\\/, '\&\&').
Якщо ви використовуєте блочну форму gsub, тобто
gsub(/\\/) { '\\\\' }, рядок для підстановки аналізується лише
один раз (під час синтаксичного проходу) і результат буде таким, як ви
й задумували.
У чому різниця між sub і sub!?
У sub створюється копія отримувача, до неї застосовується заміна,
і вона повертається.
У sub! отримувач змінюється і повертається, якщо знайдено збіг.
Інакше повертається nil.
Методи на кшталт sub!, які змінюють стан отримувача, називаються
деструктивними методами.
Зазвичай, якщо є два схожі методи і один із них деструктивний,
деструктивний має суфікс !.
def foo(str)
str.sub(/foo/, "baz")
end
obj = "foo"
foo(obj) # => "baz"
obj # => "foo"
def foo(str)
str.sub!(/foo/, "baz")
end
foo(obj) # => "baz"
obj # => "baz"
Де спрацьовує \Z?
\Z збігається безпосередньо перед останнім \n (переведенням рядка),
якщо рядок закінчується на \n, інакше — в кінці рядка.
У чому різниця між thread і fork?
This section or parts of it might be out-dated or in need of confirmation.
Потоки Ruby реалізовані всередині інтерпретатора, тоді як fork
викликає операційну систему для створення окремого підпроцесу.
thread і fork мають такі характеристики:
forkповільний,thread— ні.forkне розділяє адресний простір пам’яті.threadне спричиняє thrashing.threadпрацює на DOS.- Коли
threadпотрапляє у взаємне блокування, весь процес зупиняється. forkможе скористатися паузами очікування завершення вводу/виводу,thread— ні (принаймні без додаткової допомоги).
Ймовірно, не варто змішувати fork і thread.
Як використовувати Marshal?
Marshal використовується для збереження об’єкта у файлі або рядку з
подальшим відновленням. Об’єкти можна зберігати так:
Marshal.dump( obj [, io ] [, lev] )
io — записуваний об’єкт IO, lev задає рівень, до якого об’єкти
дереференціюються і зберігаються. Якщо виконано lev рівнів
дереференціювання і все ще існують посилання на об’єкти, тоді dump
зберігає лише посилання, а не сам об’єкт. Це погано, бо такі об’єкти
неможливо відновити згодом.
Якщо io пропущено, маршалізовані об’єкти повертаються у вигляді рядка.
Повернути об’єкти можна так:
obj = Marshal.load(io)
# or
obj = Marshal.load(str)
де io — читаний об’єкт IO, а str — збережений рядок.
Як використовувати trap?
trap пов’язує блоки коду із зовнішніми подіями (сигналами).
trap("PIPE") { raise "SIGPIPE" }