Программирование ради программирования

Недавно я решил начать читать и прорабатывать серию Семь в семи. Я не уверен, что проработаю все книги этой серии, но это начало писать об этом процессе и путешествии. Первая книга, которую нужно проработать, — Семь языков за семь недель. Это означает, что мы начинаем серию Путешествие с Руби. Это не первый раз, когда я кодирую на Ruby, в последний раз, когда я пробовал его, я писал код, как если бы Ruby был просто другим языком, таким как Java или C, но это еще не все.

Ruby — это объектно-ориентированный, динамически типизированный, интерпретируемый язык с функциональными компонентами. Язык черпает вдохновение прежде всего из Lisp, Smalltalk и Perl. Философия, лежащая в основе дизайна Ruby, кажется, создает хороший опыт для программистов, даже если это приводит к снижению производительности в других областях. Теперь, когда основная история и модные словечки рассмотрены, давайте двигаться дальше.

Как Ruby разработан для программистов?

Большинство языков программирования сделают это или подобное утверждение. Это оставляет нас с вопросом, делает ли Ruby это лучше, чем другие языки, и как он это делает. Если вы хотите, вы можете написать код Ruby в основном в том же стиле, что и языки семейства C. Хотя это вариант, это не «стиль Ruby». Есть несколько примеров дизайна языка, которые могут помочь продемонстрировать это.

//if-else
if (condition){
    print("hello");
}
//ternary operator
y = (condition) ? (x+1): (x-1)

В большинстве языков программирования вы можете выполнить условную оценку с помощью операторов if/else или тернарных операторов. Это работает, но может заставить вас писать код в определенном стиле. Этот стиль является опцией в Ruby, но это не единственный способ. В отличие от большинства языков, вы можете поместить выражение if после оператора, это также относится к некоторым циклам, таким как while. Это может лучше соответствовать тому, как люди говорят: «если x, то do this» часто читается и звучит в предложениях хуже, чем «сделай это, если condition». Вы можете не замечать этого, пока на это не укажут, как ваше дыхание, теперь, когда я упомянул об этом, вы, вероятно, думаете о дыхании и делаете это вручную, а не автоматически, как обычно.

# DEBUG is a boolean
print(x) if DEBUG

Хотя приятно иметь этот синтаксический сахар, он не сильно ускоряет или улучшает процесс кодирования. Что-то, что вы получаете гораздо больше, — это модули Ruby. Хороших примеров этого можно перечислить, они похожи на интерфейсы Java. Когда вы смешиваете модуль с классом Ruby и реализуете необходимые функции, вы можете полностью использовать этот класс как тип этого модуля, в данном случае перечисляемый. Что отличает этот процесс от Java, так это то, насколько он прост и мощен. Вместо того, чтобы реализовывать несколько методов для перечисления класса, вам нужно реализовать только один метод, each. Как только вы это сделаете, вы сможете полностью использовать этот класс как enumerable . Каждая функциональность зависит от этого единственного метода, в Java итераторы и циклы for-each похожи по поведению, но они не совсем отражают функциональную природу each. С помощью each вы можете предоставить какой-либо блок кода, и вы будете перебирать каждый элемент в перечислимом, запуская текущий элемент через свой блок кода.

arr = [1, 2, 3, 4, 5]
total = 0
arr.each {|x| total += x}

Я упомянул блоки кода в предыдущем абзаце, но не объяснил их должным образом. Возможно, вы видели нечто подобное в виде лямбда-функций или анонимных функций. Лямбда-функции и анонимные функции отличаются от стандартных тем, что у них нет идентификатора, и они часто передаются в качестве аргументов. Простой вариант использования, для которого вы можете использовать анонимные функции, — это определение функции сравнения, чтобы вы могли легко сортировать некоторый набор данных несколькими различными способами. Простым примером может служить сортировка массива людей по их имени, фамилии или идентификационному номеру. Блоки кода — это просто рубиновый стиль, блоки кода принимают форму кода, заключенного в блоки {/} или do/end. Между ними есть внутренние различия, но главное, о чем следует помнить, это стандартное соглашение об использовании {|x| puts x} для однострочных блоков и do/end для многострочных блоков.

xArr = [1, 2, 3, 4, 5]
yArr = []
arr.each do |x|
    y = x*x
    yArr.append(y)
end

Теперь о том, что Ruby позволяет, а Java — нет. Две основные особенности, о которых я хочу здесь упомянуть, — это открытые классы и метапрограммирование. Открытые классы позволяют вам открывать и изменять классы из любого места, модификаторы доступа Java работают прямо против этого, и для этого есть причины. Возможность открыть и изменить любой класс также означает, что вы можете сломать любой класс. Но эта функция удобна для изменения поведения по мере необходимости, самое близкое к этому в Java, которое приходит мне на ум, — это использование Reflector в тестировании для проверки данных, которые находятся вне видимости вашей тестовой среды.

Метапрограммирование — это написание кода, который может изменять и писать код для вас. Это может принять форму предоставления вам полезных методов без их фактического написания путем изменения method_missing . Однако эта функция может быть более надежной в других языках, таких как lisp. Однако многие достижения в языках программирования просто добавляли возможности lisp к новым языкам.

Насколько улучшился Ruby с тех пор, как была написана книга «Семь языков за семь недель»?

Прошло около десяти лет с тех пор, как была написана книга «Семь языков за семь недель», и можно с уверенностью предположить, что существуют различия между Ruby того времени и Ruby сегодняшнего дня. В книге используется Ruby версии 1.8.7, на момент написания текущей стабильной версии является 3.1.2. После всего этого времени должны быть улучшения как в сильных, так и в слабых сторонах Ruby. Основными моментами, на которых я собираюсь сосредоточиться, являются производительность и параллелизм.

Производительность

Похоже, за это время производительность значительно улучшилась. Основное улучшение заключается в том, что Ruby 3.0 стал в 3 раза быстрее, чем Ruby 2.0. Другие улучшения связаны с альтернативными методами запуска кода Ruby, которые представлены в виде различных интерпретаторов и систем компиляции. Некоторые из этих вариантов компилируют и запускают код Ruby через другую более эффективную с точки зрения вычислений систему, такую ​​как виртуальная машина Java.

параллелизм

Мое исследование этого довольно ограничено, так как я не пробовал программировать на Ruby. Язык имеет некоторые встроенные функции параллелизма, но они довольно просты. Если вы хотите написать более надежный параллельный код, вам нужно найти жемчужины, которые предоставляют вам механизмы, необходимые для вашей задачи. Больше всего мне приходит на ум https://github.com/ruby-concurrency/concurrent-ruby, целью которого является предоставление Ruby-реализаций механизмов параллелизма, которые можно найти в других языках. Я разочарован тем, что базовый язык недостаточно развит в этом отношении, но в гемах можно найти достаточно базовые механизмы параллелизма, которых должно быть достаточно, чтобы заполнить этот пробел на данный момент. Было бы хорошо, если бы параллельные реализации основных структур данных Ruby стали частью языка, а также такие инструменты, как блокировки и пулы потоков, поскольку они могут значительно улучшить возможности параллельного кодирования.