Ruby/Идеология: различия между версиями

Содержимое удалено Содержимое добавлено
м <source> -> <syntaxhighlight> (phab:T237267)
Строка 62:
 
В большинстве случаев, можно обойтись без циклов. Было бы желание. Для примера перепишем одну из программ, представленных на вышеописанном сайте.
<sourcesyntaxhighlight lang=ruby>m, n = 2, 2
while m <= 5
m, n = m + 2, n + m
end
puts "m=#{m}"
puts "n=#{n}"</sourcesyntaxhighlight>
Ее можно переписать в виде:
<sourcesyntaxhighlight lang=ruby>n = 2
2.step(5,2){ |m| n += m }
puts "n=#{n}" # m - локальная переменная внутри блока, вне блока ее не существует</sourcesyntaxhighlight>
Стоит заметить, что во втором фрагменте мы избавились от ненужной глобальной переменной, которую превратили в локальный счетчик итератора. При этом была использована информация о конечном значении счетчика. Стоит ли использовать циклы, если они порождают массу проблем? А именно:
* Ухудшение читабельности кода.
Строка 150:
Для начала, немного теории. В Ruby (а также в Perl) существует метод <code><=></code>, широко применяемый в различных итераторах (<code>sort</code>, <code>max</code>, <code>min</code> и так далее). По сути, он включает в себя всевозможные операции сравнения (<code><</code>, <code>></code>, <code>>=</code>, <code><=</code>, <code>==</code> и <code>!=</code>) и может замечательно их заменять. Работа этого метода заключается в том, что он возвращает целое число (-1, 0 или 1), в зависимости от результата сравнения. Пример:
 
<sourcesyntaxhighlight lang=ruby>3 <=> 5 #-> -1
5 <=> 3 #-> 1
3 <=> 3 #-> 0
5 <=> 5 #-> 0</sourcesyntaxhighlight>
 
Как видно из примера, в случае, если левый агрумент меньше правого, то метод <code><=></code> возвращает -1, если больше, то 1, а если они равны, то 0. Именно это свойство метода <code><=></code> я и собираюсь использовать.
Строка 159:
Но для "эквивалентной замены" условного оператора этого недостаточно. Требуется что-то еще... Это что-то называется "возможность отрицательной индексации в массивах", то есть в Ruby массивы обладают возможностью индексировать свои элементы не только с 0 до N-1, но и с -N до -1, где N — размер массива. Последний элемент будет иметь индекс -1, предпоследний -2 и так далее. Причем, отрицательная индексация уже включена и для ее применения никаких дополнительных действий не требуется. Пример:
 
<sourcesyntaxhighlight lang=ruby>Array = [1, 2, 3, 4, 5]
Array[0] #-> 1
Array[-5] #-> 1
Array[-1] #-> 5
Array[4] #-> 5</sourcesyntaxhighlight>
 
Эта возможность появилась как следствие патологической лени программистов. Для понимания их логики, продемонтрирую пример, который покажет, как выглядел бы этот же самый код, если бы возможность отрицательной индексации отсутствовала. Пример:
 
<sourcesyntaxhighlight lang=ruby>Array = [1, 2, 3, 4, 5]
Array[0] #-> 1
Array[Array.size - 5] #-> 1
Array[Array.size - 1] #-> 5
Array[4] #-> 5</sourcesyntaxhighlight>
 
Метод <code>.size</code> возвращает количество элементов массива и его использование было бы необходимым, но в результате его использования код становится более громоздким, поэтому программисты решили эту проблему со свойственным им изяществом. Вернемся к исходной теме нашего повествования...
Строка 177:
Применим описанные выше свойства для демонстрации "альтернативного условного оператора" (сокращенно АУО). Для наглядности, решим задачку: "Даны два числа. Вывести слово 'больше', если первое число больше второго, 'меньше', если оно меньше. В случае равенства этих чисел, нужно вывести слово 'равны'." Решение:
 
<sourcesyntaxhighlight lang=ruby>number_one, number_two = 4, 5
puts ['равны','больше','меньше'][number_one <=> number_two]</sourcesyntaxhighlight>
 
Вполне естественно, что задача сугубо специфическая и придумана специально для пояснения сути АУО, но изящество, с которым она решена, позволяет об этом временно забыть. =)
Строка 184:
А что будет, если надо использовать не три варианта, а меньше? Хм. Вопрос вполне уместный. Давайте попытаем задачу: "Даны два числа. Вывести слово 'равны', если они равны и словно 'неравны', иначе." Возможное решение:
<sourcesyntaxhighlight lang=ruby>number_one, number_two = 4, 5
puts ['равны','неравны','неравны'][number_one <=> number_two]</sourcesyntaxhighlight>
 
Данное решение неоптимально, поскольку задействует дополнительную память для хранения дублирующего ответа. Первое, что приходит на ум — сократить массив до двух элементов. Решение:
<sourcesyntaxhighlight lang=ruby>number_one, number_two = 4, 5
puts ['равны','неравны'][number_one <=> number_two]</sourcesyntaxhighlight>
 
Красиво? Вот и я так думаю. Давайте посмотрим, как это получилось. Про элемент 'равны' вопросов возникать не должно. Но как быть со вторым вариантом? Дело в том, что второй вариант имеет два индекса 1 и -1, которым как раз и соответствует неравенство. Справедливости ради, заметим, что элемент 'равны' также имеет два индекса (0 и -2), но метод <code><=></code> значения -2 не принимает.
Строка 228:
Теперь надо как-то назвать эту структуру. После недолгих раздумий, было решено именовать ее Contract и построить ее на базе класса Hash, добавив к нему всего два метода: <code>.add_contract</code> и <code>.find_contracts</code>. Вот их реализация и коротенький пример:
 
<sourcesyntaxhighlight lang=ruby>class Hash
def add_contract(key, &block)
self[ key ] = block
Строка 244:
hash = {}
hash.add_contract( 5 ){ |i| puts i }
p hash.find_contracts{ |i| i == 5 } #-> [#<Proc:0x02777bf8@test019.rbw:40>]</sourcesyntaxhighlight>
 
Как видно, результатом является массив "проков", где для обработки каждого "прока" надо вызвать метод <code>.call</code>. [[Участник:Rubynovich|Rubynovich]] 19:07, 9 июня 2006 (UTC)
Строка 263:
Контролировать тип входных данных, которые передаются в метод, можно по-разному. Например, вызывая метод <code>.class</code>, который возвращает класс, к которому принадлежит передаваемый объект.
 
<sourcesyntaxhighlight lang=ruby>def meTog( napameTp_1 )
raise "Не тот тип входных данных!" if [<u>String</u>, <u>Array</u>].include?( napameTp_1<u>.class</u> )
case napameTp_1.class
Строка 271:
napameTp
end.map{ |s| s.to_i }
end</sourcesyntaxhighlight>
 
{{info|Метод <code>.class</code> возвращает объект класса <code>Class</code>, то есть код <code><nowiki>o6bekT.class.class == Class</nowiki></code> будет всегда возвращать <code>true</code>}}
Строка 283:
Давайте реализуем предыдущий код, но с использованием расширения класса.
 
<sourcesyntaxhighlight lang=ruby>class String
def meTog
split('')<u>.meTog</u>
Строка 295:
end
napameTp_1.meTog</sourcesyntaxhighlight>
 
В зависимости от класса, будет вызван либо один <code>meTog</code>, либо другой. При этом, <code>meTog</code> в классе <code>String</code> используется как адаптер для <code>meTog</code>'а класса <code>Array</code>. Единственный недостаток такого подхода — случайное переопределение базовых методов. Но эта случайность быстро отлавливается. [[Участник:Rubynovich|Rubynovich]] 17:40, 5 ноября 2006 (UTC)
Строка 313:
Тут же появилась идея реализовать программу, которой потенциально мог воспользоваться Vixen. Первым прислал решение [http://vkontakte.ru/id418587 Руслан Мухаметов]:
 
<sourcesyntaxhighlight lang=ruby>#!/usr/bin/ruby
# Программа для написания чисел на русском языке из цифрового представления
# Версия 0.1.1
Строка 405:
end.reverse.compact.join(" ")
 
puts ans</sourcesyntaxhighlight>
 
Прочитав этот код я ничего не понял. А самый верный способ понять код — провести рефакторинг. Что я и решил сделать:
 
<sourcesyntaxhighlight lang=ruby>#!/usr/bin/ruby
# Программа для написания чисел на русском языке из цифрового представления
# Версия 0.2
Строка 463:
end + " " + threes_names[i-1] + ok[i][ (11...20).include?(n % 100) ? 0 : n % 10 ] if n.nonzero?
}
}.compact.reverse.push(nums[threes[0].to_i]).join(" ")</sourcesyntaxhighlight>
 
Сейчас пока некогда рассказывать как это получилось, но чуть позже опишу свои действия по шагам. [[Участник:Rubynovich|Rubynovich]]
Строка 469:
Еще один вариант решения:
 
<sourcesyntaxhighlight lang=ruby>#!/usr/bin/ruby
# Программа для написания чисел на русском языке из цифрового представления
# Версия 0.3
Строка 523:
threes.each_with_index{|x,i| x.to_i>0 ? r<<[nums[x.to_i-1],threes_names[i]+ok[i][(11...20)===(x.to_i%100) ? 0 : x.to_i%10]] : r<<[nums[x.to_i-1]]}
 
p r.reverse.join(' ').sub(/один тысяча/, 'одна тысяча').sub(/два тысячи/, 'две тысячи').gsub(/\s+/,' ').chop</sourcesyntaxhighlight>
 
== См. также ==