Первый взгляд на Ruby

Новогодние каникулы оказались подходящим случаем для общего знакомства с Ruby. Особой склонности к языкам с динамической типизацией я не испытываю — применимость их в больших проектах весьма сомнительна. В Ruby же привлекли возможности метапрограммирования и их использование для реализации DSL (Domain Specific Languages). Кроме того, хотелось понять, что позволяет преодолеть недостатки динамической типизации при использовании Ruby в достаточно больших проектах.


Ruby — интерпретируемый язык (это не значит, что для него нельзя разработать компилятор. Но думать о Ruby проще как об интерпретируемом языке). Это накладывает отпечаток на схему организации и выполнения Ruby-программ. В этом смысле они ближе к shell-скриптам, чем к программам на «серьезных» языка типа C/C++/Java — Ruby интерпретатору подается программа (из файла или из стандартного входа), которая немедленно исполняется. Нет никаких деклараций, которые предварительно как-то обрабатываются (компилируются), а затем используются при выполнении программы — что вижу, о том и пою.

% ruby
puts "Hello, world!"
^D
Hello, world!

Это не значит, что деклараций нет вообще. Ruby — объектно-ориентированный язык, естественно, он позволяет определять классы. Но правильнее думать о классах как о немедленных указаниях интерпретатору выполнить определенные действия — зарегистрировать класс с заданным именем. Так же дела обстоят и с определением методов — это указание зарегистрировать в текущем классе метод с заданным именем.

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

В свое время мне довелось оптимизировать ассемблерный код, сгенерированный компилятором языка С (кажется, DECUS С на СМ-4, то есть PDP-11). C тех пор код на С и частично на С++ я автоматически перевожу на ассемблер. Это сильно помогает понять, что же будет происходить во всяких нетривиальных ситуациях. Вот и с Ruby наблюдается похожая ситуация. Обязательно разберитесь с вычислительной моделью!

Ruby — объектно-ориентированный (ОО) язык. К сожалению, одной этой фразы мало — под крышей ОО собрались весьма непохожие друг на друга языки. Так что будем разбираться дальше. Одно можно сказать наверняка — в соответствии с классическим определением ОО в Ruby есть объекты. Этим объектам можно посылать сообщения. Ссылку на объект можно сохранить в переменной. Посылка сообщения соответствует вызову метода. Ну вот, уже что-то общее с широко используемыми языками.

class Klass
  def hello( addressee )
    return "Hello, " + addressee + "!"
  end
end
 
k = Klass.new()
puts( k.send( :hello, "world" ) ) # Послать сообщение :hello
puts( k.hello( "world" ) )        # то же самое, что вызвать метод hello

Хорошие новости — сообщения могут иметь параметры и возвращать значение.

Литеральными значениями могут быть заданы числа, строки, массивы, хэши, диапазоны, символы и регулярные выражения. Всё это — полноценные объекты. Никаких примитивных типов, как в Java, всё по-честному.

Числа имеют произвольную длину. Если не помещаются в машинное слово — автоматически используется реализация длинной арифметики. А чтобы проще было писать длинные литералы, разрешается использовать подчеркивания, интерпретатор их игнорирует. Для чисел с плавающей точкой используется машинный double.

PI_INTEGER = 3_141_592_653_589_793_238_462_643_383_279_502_884

В строках самая примечательная возможность — подстановка значения выражения, которое находится внутри #{...}. Как мне не хватало этого в Java!

Если не хотите думать об экранировании спецсиволов — используйте одинарные кавычки.

addressee = "world"
puts( "Hello, #{addressee}!" ) # напечатает Hello, world!
puts( 'Hello, #{addressee}!' ) # Напечатает Hello, #{addressee}!

Символы — это объекты, представляющие имена, используемые в программе. Отличаются от имен двоеточием в начале. В примере с посылкой сообщения, которое соответствует методу hello, был использован символ :hello. В Java пришлось бы использовать текстовую строку, что чревато описками.

  1. Для использования символа имя не обязательно должно быть определено.
  2. C/Java символы в одинарных кавычках — это совсем не то, не путать.

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

Далее план такой (может и поменяться):

  1. Классы и модули
  2. Методы и блоки
  3. Обработка неизвестных сообщений
  4. Псевдо-DSL
  5. Инструменты (будут подпункты)

13.01.2008  Метки:   Рубрики: Разработка, Языки

Написать комментарий