Что мне нравится в ruby

Что мне нравится в ruby
За что я люблю язык программирования руби. Ключевые особенности. Красота разработки ruby

Обновляя уже полученные знания и продолжая изучать руби, возникла потребность упорядочить изученное и не забыть. Это подвигло меня к написанию нескольких свежих материалов посвященную ключевым аспектам и особенностям разработки на руби. В этой статье я хотел бы выделить те особенности языка, которые являются для меня особо привлекательными на данный момент. Вполне вероятно что и многие читатели будут со мной солидарны. Итак что мне нравится в руби:

  1. Красота и лаконичность кода.

Об этом конечно уже много всего написано, и это не является характерным только для этого языка (разработчики Python например тоже часто приводят это в качестве аргумента в пользу их любимого ЯП), но все же эту идеологию так называемого "dry code" (Dont Repeat Youself code - не повторяющийся код) нельзя не упомянуть так как ее культивируют все продвинутые рубисты.

Собственно и сама архитектура языка соответствует принципам максимального минимализма. Рассмотрим пример часто используемых процедур разбиения/объединения строк и массивов:

/* Вот так мы собираем/разбираем строки на PHP (это лишь упрощенная демонстрация
   типичных последовательности действий, понятно что с реальной задачей не имеет ничего общего) */ 

$arr = explode(",", "1,2,3,4,5");
$arr[] =  6;
echo implode(",", $arr);
#Тоже самое на руби:

"1,2,3,4,5".split(",").push("6").join(",")

Возможность функциональный подхода, краткие имена методов, наличие встроенных механизмов генерации кода (ниже я напишу о блоках) и т.д. позволяют упростить процесс рефакторинга, или даже полностью избавить от подобной необходимости.

  1. Ruby динамический язык. В том смысле что объекты и классы можно изменять во время исполнения.

Впринципе для меня, знакомого с таким языком как javascript в этом нет ничего необычного. Однако я считаю это большим плюсом для серверного языка программирования. PHP подобными фишками похвастаться, увы не может. Мы на лету можем переопределять базовые методы языка, добавлять к базовым объектам и классам свои методы. Естественно с такими вещами необходимо быть внимательными. Пример:

class Array                                         #добавляем метод cube к базовому классу Array
	def cube                                        #метод возвращает массив, состоящий из  
	  arr = []                                      #элементов возведенных в кубическую степень. 
	  each { |elem| arr << elem**3 }	
	  return arr
	end
end	
[1,2,3,4,5].cube                                        #=> [1, 8, 27, 64, 125]  
  1. Многие операторы в руби не являются операторами как таковыми. Это всего лишь сокращенная форма записи методов.

Благодаря этой особенности и динамической природе руби мы получаем возможность переопределения операторов! Лично я такой гибкости не встречал в известных мне языках, и эта особенность меня очень порадовала. Например мы можем выделывать подобные фокусы: 

class Array 
   def +(var)		
	 arr = clone		
	 case var
	    when Integer 
			arr.map! { |elem| elem.kind_of?(Integer) ? elem + var : elem }		
		when Array      
			arr.concat(var)	  
		end   
	 return arr
	end
end

["a",2,3,4,5] + 5                                         #=> ["a", 7, 8, 9, 10]
["a",2,3,4,5] + ["a","b","c"]                             #=> ["a", 2, 3, 4, 5, "a", "b", "c"]

Базовый класс Array уже содержит метод "+", который принимает другой массив в качестве аргумента и возвращает массив полученный путем слияния этих двух массивов. Но если переданный методу аргумент не является массивом произойдет ошибка. В этом примере я изменил этот метод так чтобы он увеличивал значения каждого элемента массива (складывая числа) если в качестве аргумента ему передается число.

  1. Встроенные итераторы.

Для того чтобы в php использовать итераторы был созданы отдельные классы и интерфейсы. Использовать их весьма накладно, необходимо тщательно изучить все методы и классы и написать немалое количество кода в стиле ООП. Для повседневных задач все это не очень подходит. В руби же итераторы встроены. Более того они могут полностью заменить собой циклы:

ist.each do |x|            #итератор "each"
   print "{#x }"
end

n = list.size              #итератор "times"
n.times do {i}
   print "#{list[i] }"
end  

n = list.size - 1          #итератор "upto"
0.upto(n) do |i|
   print "#{list[i] }"
end
  1. Блоки и объект Proc. 

Блоки в руби, на мой взгляд очень полезная штука. Многие задачи с помощью блоков можно решить проще и изящнее, можно использовать все преимущества динамической сущности руби, написав при этом минимум кода. Многие так называемые "магические методы" реализованы с помощью блоков. Очень часто приводят следующий пример:

first_line = File.open("some.txt") do |file|
   file.readline
end

Методу open системного класса File передается блок в качестве параметра, который в нашем случае считывает и возвращает первую строку файла. Все остальное (создание дескриптора файла, корректное закрытие файла, генерация исклюбчений в случае ошибки и т.д. ) происходит в нем неявно. Просто в каком то он передает управление пользовательскому блоку переданному ему в качестве параметра. Вы только задаете комплекс действий который необходимо произвести после открытия файла, заботу обо всем остальном берет на себя метод open.

Еще один пример. Например нам необходимо задавать(или даже динамически генерировать) обработчики для каких нибудь событий в методе. С помощью блоков это можно легко организовать:

def doSomething callbacks                #выполняются какие то действия
 unless callbacks.empty?                 #в результате которых вызывается
    ...                                  #то или иное событие (в моем примере оно рандомно)
  events = callbacks.values     
    callback = events[rand(events.size)]  
    callback.call
  end
end 

callbacks = { 
        success: proc { print "Process success.." },
	fail: proc { print "Process fail.." } 
}

doSomething(callbacks)
doSomething success: proc { puts "success!" },  progress: proc { puts "in progress.." } 

callbacks = {}                          #пример динамического создания блоков. метод strBlock
                                        #возвращает строку, которая описывает тело блока
def strBlock(action)					
    '"#{' + action + '} event started.."'                           
end

#итератор генерирует массив блоков из строк
          
%i(success fail).each do |events|       
    callbacks[events] = eval "proc { " + strBlock("events") + " }"    
end

doSomething callbacks

Последний пример(создание блоков на лету) возможно покажется притянутым за уши. Однако, я подозреваю что подобные фокусы часто используются в ruby. Также блоки широко применяются в итераторах и могут применятся в качестве замыканий:

(1..5).each { |i| puts i } #итератор each принимает блок, который печатает переменные из диапазона

def twice(name)              #пример замыкания. метод twice дважды выполняет блок
   2.times { yield name }    #ключевое слово yield передает управление блоку,
end                          #запоминая локальную переменную метода(замыкается переменная name)  

twice("Ivan")  { |name| puts name }              #печатает два раза "Ivan"	
twice("Vasya") { |name| puts "Hello, #{name}!"}  #печатает два раз "Hello, vasya!" 

def exp(x)                 #еще один пример замыкания, функция возведения в степень
    proc { |y| y**x } 	
end	

square = exp(2)
cube = exp(3)
cube.call(5)                           #=> 125
square.call(5)                         #=> 25

Как видно из примеров область применения блоков довольно обширная. Конечно нечто подобное реализовано и в других языках. В последних версиях php тоже есть возможность создания анонимных функции и замыканий но реализация этого откровенно говоря далека от идеала.  

  1. Наличие интерактивной консоли в которой можно быстро протестировать код

О да, все эти примеры можно скопипастить в интерактивную консоль ruby и покрутить там. Также в руби реализован неплохой механизм отражений, который позволяет исследовать основные объекты, их методы и классы. Возможно я опишу их более подробно в отдельной статье, в которой составлю список наиболее полезных методов. Таким образом можно легко и быстро погрузится в огромное пространство языка. 

Напоминаю, что целью статьи было продемонстрировать и документировать основные и привлекательные с моей точки зрения аспекты руби а не с целью опорочить другие языки, как может показаться.

Конечно у него есть и свои минусы, такие как отсутствие "прозрачности" во многих библиотеках, не самой лучшей по слухам документации (хотя меня на данном этапе устраивает) и т.д. Как бы то ни было, если для кого-то эта статья послужит причиной для освоения чего-то нового, это будет хорошим бонусом к моей карме ну и к вашим способностям разумеется. Полезных вам знаний и успехов в разработке!

Комментарии

Нет комментариев

Добавить комментарий