Язык программирования Clojure

Применение Clojure-плагина для Eclipse

Данная статья рассказывает о языке програмирования Clojure, представляющем собой функциональный диалект Lisp. Статья не предполагает знания Lisp, однако вы должны иметь представление о Java- технологиях. Для написания программ на Clojure вам потребуется Java Development Kit V5 или выше и библиотека Clojure. При создании примеров, рассматриваемых в статье, использовалась JDK V1.6.0_13 и Clojure V1. Также вам потребуется плагин Clojure для Eclipse, и, соответственно, сама среда разработки Eclipse. Материал данной статьи основан на использовании Eclipse V3.5 с плагином clojure-dev 0.0.34. Ссылки на соответствующие дистрибутивы можно найти в разделе Ресурсы.

Майкл Галпин, инженер по программному обеспечению, Vitria Technology

Майкл Галпин (Michael Galpin) имеет учёную степень по математике в Калифорнийском Технологическом институте. Он является Java-разработчиком с конца 90-х гг. и работает инженером по программному обеспечению в Vitria Technology, в Саннивейл, Калифорния.



23.12.2009

Что такое Clojure?

Совсем недавно для того, чтобы запустить программу на Java Virtual Machine (JVM), необходимо было написать эту программу на языке Java. Эти дни безвозвратно ушли в прошлое, потому что теперь есть широкий выбор языков программирования для JVM. Наиболее популярные, такие как Groovy, Ruby (с интерпретатором JRuby), и Python (с интерпретатором Jython), предназначены в основном для процедурного программирования или же относятся к группе объекто-ориентированных языков. Парадигмы процедурного и объекто-ориентированного программирования хорошо знакомы Java-программистам, так что сторонники Java имеют веский аргумент в свою пользу – используя вышеназванные языки, вы напишете примерно такой же код, какой вы написали бы на Java, вам просто придется использовать другой синтаксис.

Clojure – тоже язык программирования для JVM, однако он в корне отличается от Java-технологии и других упомянутых выше языков программирования для JVM. Clojure – диалект Lisp. Семейство языков программирования Lisp существует уже довольно длительное время – фактически с 50-х годов прошлого века. Lisp использует S-выражения, или польскую префиксную запись. Такая запись выглядит как (function arguments...). Вы всегда начинаете с задания имени функции, за которым следует список (пустой или непустой) аргументов, передаваемых функции. Чтобы понять, где начинается и где заканчивается определение функции, ее имя и список аргументов заключаются в скобки. Именно это требование синтаксиса языка привело к появлению огромного количества скобок в коде, что стало своего рода торговым знаком Lisp.

Как можно догадаться, Clojure – функциональный язык программирования. С чисто академической точки зрения его «чистоту» можно оспаривать, однако Clojure, безусловно, опирается на основные принципы функционального программирования: отказ от изменяемых переменных (mutable state), использование рекурсии и функций высокого порядка и т.п. В Clojure используется динамическая типизация, однако при необходимости вы можете задать определенный тип данных, чтобы обеспечить максимальную производительность критически важных фрагментов кода. Clojure не просто работает на JVM, он разрабатывался с учетом операционной совместимости с Java. Кроме того, создатели Clojure ориентировались на поддержку многопоточности, так что Clojure предоставляет разработчикам уникальные возможности для параллельного программирования.


Clojure в примерах

Для большинства программистов лучший способ освоить новый язык– это начать писать на нем программы. Поэтому мы рассмотрим несколько простых проблем, с которыми часто сталкиваются разработчики программ, и попробуем их решить с помощью Clojure. Детальный разбор решений поможет вам лучше понять, как работает Clojure, как его использовать, и для каких задач он наиболее подходит. Для работы с Clojure, как и для других языков программирования, сначала необходимо установить соответствующую среду разработки. К счастью, в случае Clojure сделать это довольно просто.

Минимальные требования

Все, что вам нужно для работы с Clojure, - это JDK и библиотека Clojure, которая содержится в одном JAR-файле. Существует два стандартных способа разработки и запуска программ на Clojure. Наиболее распространенный способ – это использование простой интерактивной среды разработки REPL, цикла «чтение-вычисление-печать».

Листинг 1. Запуск Clojure REPL
$ java -cp clojure-1.0.0.jar clojure.lang.Repl
Clojure 1.0.0-
user=>

Команда должна выполняться в директории, где расположен JAR-файл Clojure. Если вам это неудобно, добавьте путь до соответствующего JAR-файла к своим стандартным путям поиска. Вместо «ручного» запуска команды REPL вы можете написать скрипт, который будет выполнять эту команду, и просто запускать свой скрипт. Для этого вам надо будет вызвать Java-класс clojure.main.

Листинг 2. Вызов clojure.main.
$ java -cp clojure-1.0.0.jar clojure.main /some/path/to/Euler1.clj 
233168

Не забудьте определить путь к JAR-файлу Clojure и к вашему скрипту. Наконец, существует интегрированная среда разработки для Clojure. Пользователи Eclipse могут скачать и установить плагин clojure-dev. После установки плагина переключитесь в Java-перспективу. Теперь вы можете создать новый Clojure-проект и добавить в него файлы с исходным кодом Clojure, как показано на рисунке:

Рисунок 1. Использование плагина clojure-dev в Eclipse
Using clojure-dev, the Clojure plug-in for Eclipse

Установив плагин clojure-dev, вы сможете использовать синтаксические подсказки и встроенные проверки Eclipse, в том числе для обеспечения соответствия количества открывающих и закрывающих скобок (функциональность, абсолютно необходимая при работе с любой разновидностью Lisp). Кроме того, Eclipse включает в себя интерактивную среду REPL, так что вы сможете выполнить любой скрипт в командном окне REPL в Eclipse. Плагин Clojure был выпущен незадолго до написания этой статьи, однако его функциональность очень быстро развивается. Теперь, когда у нас есть необходимая среда разработки, можно приступить к созданию программ на Clojure.


Пример 1. Работа с последовательностями

Название языка Lisp – это аббревиатура «list processing» – обработка списков. Очень часто говорится, что все объекты Lisp – это списки. В диалекте Clojure списки обобщены в последовательности. В качестве первого примера рассмотрим следующую задачу программирования.

Рассмотрим все натуральные числа меньше 10, которые делятся на 3 или на 5 – это числа 3, 5, 6 и 9. В сумме они дают 23. Теперь найдем сумму всех чисел меньше 1000, которые кратны 3 или 5.

Эта задача опубликована на сайте Project Euler. Проект Project Euler содержит коллекцию математических проблем, которые могут быть решены с использованием хорошо продуманных (или не очень хорошо продуманных) компьютерных программ. Рассматриваемая здесь задача – первая (и сама простая) в списке Project Euler. Листинг 3 предлагает ее решение на Clojure.

Листинг 3. Задача 1 из списка Project Euler
Первая строка листинга определяет функцию. 
Запомните: функции – основные строительные блоки программы на Clojure. 
Большинство Java-программистов привыкли к тому, 
что базовыми элементами программы являются объекты, 
так что, возможно, вам понадобится некоторое время, 
чтобы приспособиться к разработке программ, 
основными элементами которых являются функции. 
Вы можете подумать, что defn – это служебное слово языка Clojure, 
но на самом деле это макрос. 
Макросы позволяют расширить функции компилятора Clojure
 - в основном путем добавления новых ключевых слов.
  Таким образом, defn не определяется спецификацией языка, 
  а добавляется при подключении основной библиотеки Clojure.

В нашем случае макрос defn определяет функцию divisible-by-3-or-5?. Имя функции выбрано в соответствии с конвенцией выбора имен в Clojure. Слова разделяются дефисами, а в конце имени функции стоит вопросительный знак, который означает, что функция возвращает логическое значение «ложь» или «истина». Функции передается единственный аргумент num. Если бы нам потребовалось передать функции больше одного параметра, все параметры надо было бы перечислить в квадратных скобках, разделяя их пробелами.

За объявлением следует тело функции. Сначала мы вызываем функцию or. Это обычное логическое выражение or с той лишь разницей, что это не оператор, а функция. Мы передаем функции or два параметра. Каждый параметр, в свою очередь, тоже является выражением. Первое выражение начинается с функции ==. Это функция, которая сравнивает значения своих аргументов. В нашем примере функции сравнения передаются 2 параметра. Первый – опять-таки выражение, которое вызывает функцию mod. Это обычное математическое деление по модулю, соответствующее оператору % в Java. Функция mod возвращает остаток от деления, т.е. в нашем случае, остаток от деления числа num на 3. Затем этот остаток сравнивается с 0. Если остаток равен 0, то число num делится на 3. Аналогично мы проверяем, делится ли число num на 5. Если остаток от деления на 3 или на 5 равен 0, то функция divisible-by-3-or-5? возвращает значение «истина».

В следующей строчке мы вычисляем выражение и выводим его на печать. Начнем разбор выражения с самых внутренних скобок. Здесь мы вызываем функцию range и передаем ей число 1000. Функция создает последовательность целых чисел от 0 до 999 включительно. Это именно те числа, чью делимость на 3 и 5 мы должны проверить. Далее переходим к следующему уровню вложенности. Здесь мы вызываем функцию filter с двумя аргументами. Первый аргумент должен быть логическим выражением, принимающим значение «истина» или «ложь», а второй аргумент — последовательностью. В нашем примере это последовательность (0, 1, 2, ... 999). Функция filter вычисляет логическое выражение, и если оно истинно, соответствующий элемент последовательности добавляется к результату. Логическое выражение – это функция divisible-by-3-or-5?, которую мы подробно разобрали выше.

Таким образом, результатом работы функции filter будет последовательность целых чисел меньше 1000, кратных 3 или 5. Это именно те числа, которые нам нужно найти. Теперь для решения задачи остается только получить сумму этих чисел. Для этого мы используем функцию reduce. Эта функция использует 2 параметра: функцию-аргумент и последовательность. reduce применяет функцию-аргумент к первым двум элементам последовательности, а затем проходит по последовательности, каждый раз применяя функцию-аргумент к предыдущему полученному результату и следующему элементу последовательности. В нашем примере в качестве функции-аргумента выступает функция +, то есть обычное сложение. Таким образам, на выходе мы получаем сумму всех элементов последовательности.

Взгляните на листинг 3 - просто поразительно, сколько действий выполняет такой крошечный фрагмент. Однако наше пространное описание работы кода не должно вас пугать. Как только вы освоитесь с префиксной записью, код станет для вас вполне очевидным. Безусловно, если использовать Java для решения этой же задачи, программа будет гораздо длиннее. Перейдем к следующему примеру.


Пример 2. Лень - это добродетель

Следующий пример поможет нам освоить использование рекурсии и «ленивых» (lazy) вычислений в Clojure. Концепция рекурсии и «ленивых» вычислений, возможно, также окажется новой для большинства Java-программистов. Clojure позволяет определять «ленивые» последовательности, элементы которых вычисляются только тогда, когда это необходимо. Это дает возможность использовать бесконечные последовательности, что невозможно в Java. Чтобы разобраться, где и как именно применяются «ленивые» последовательности, рассмотрим задачу, решение которой основано на использовании еще одного важного аспекта функционального программирования: рекурсии. Вновь обратимся к коллекции Project Euler; на этот раз возьмем задачу №2.

Каждый новый член последовательности Фибоначчи является суммой двух предыдущих членов последовательности. Если мы начнем ряд с чисел 1 и 2, то получим следующие десять первых элементов ряда: 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,...

Найдите сумму всех четных чисел последовательности Фибоначчи, которые не превышают 4 миллионов. Для решения этой проблемы Java-программист, скорее всего, определил бы функцию, которая вычисляет n-й элемент последовательности Фибоначчи. Несколько наивная реализация такой функции приведена в листинге 4.

Листинг 4. Наивная функция Фибоначчи
(defn fib [n] 
    (if (= n 0) 0
        (if (= n 1) 1
            (+ (fib (- n 1)) (fib (- n 2))))))

проверяет, равно ли n 1. Если это так, то функция возвращает 1. Для всех остальных значений n функция вычисляет (n-1)-й и (n-2)-й элементы последовательности Фибоначчи и складывает их. Все это выглядит абсолютно правильным, однако если у вас есть достаточно большой опыт программирования на Java, то вы уже нашли проблему. Рекурсивное определение в приведенном выше примере очень быстро заполнит весь стек и приведет к переполнению. Числа Фибоначчи образуют бесконечную последовательность, и, будучи таковыми, должны определяться как бесконечная «ленивая» последовательность Clojure. Такое определение показано в листинге 5. Здесь необходимо заметить, что, стандартная библиотека clojure-contrib включает более эффективное определение последовательности Фибоначчи, однако библиотечная функция является довольно сложной для понимания, так что мы в данной статье воспользуемся примером из книги Стюарта Хэлловея (Stuart Halloway) - подробную информацию можно найти в разделе Ресурсы.

Листинг 5. «Ленивая» последовательность для чисел Фибоначчи
(defn lazy-seq-fibo 
    ([] 
        (concat [0 1] (lazy-seq-fibo 0 1))) 
    ([a b] 
        (let [n (+ a b)] 
            (lazy-seq 
                (cons n (lazy-seq-fibo b n))))))

В листинге 5 функция lazy-seq-fibo определяется дважды. Первое определение не имеет аргументов, отсюда пустые квадратные скобки. Второе определение использует два аргумента [a b]. Для безаргументного определения мы используем последовательность [0 1] и преобразовываем ее в выражение. Выражением является рекурсивный вызов функции, но при этом вызывается уже функция с аргументами 0 и 1.

Определение функции с двумя аргументами начинается с выражения let. Это способ задания переменных в Clojure. Выражение [n (+ a b)] определяет переменную n и задает ее значение, равное a+b. Затем используется макрос lazy-seq. Как следует из имени макроса, он создает «ленивую» последовательность. Телом макроса является выражение. В нашем примере, выражение обращается к функции cons. Это классическая функция Lisp. Функция использует элемент последовательности и саму последовательность и возвращает новую последовательность, добавляя элемент к началу последовательности. В нашем случае, последовательностью является результат вызова функции lazy-seq-fibo. Если бы последовательность не была бы ленивой, функция lazy-seq-fibo вызывалась бы снова и снова. Однако благодаря макросу lazy-seq функция lazy-seq-fibo будет вызываться только тогда, когда элементы последовательности используются. Для того чтобы увидеть, как работает такая последовательность, воспользуйтесь интерактивным режимом REPL, как показано в листинге 6.

Листинг 6. Генерация чисел Фибоначчи
1:1 user=> (defn lazy-seq-fibo 
    ([] 
        (concat [0 1] (lazy-seq-fibo 0 1))) 
    ([a b] 
        (let [n (+ a b)] 
            (lazy-seq 
                (cons n (lazy-seq-fibo b n))))))
#'user/lazy-seq-fibo
1:8 user=> (take 10 (lazy-seq-fibo))
(0 1 1 2 3 5 8 13 21 34)

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

Листинг 7. Задача 2
(defn less-than-four-million? [n] (< n 4000000))

(println (reduce + 
    (filter even? 
        (take-while less-than-four-million? (lazy-seq-fibo)))))

В листинге 7 мы определяем функцию less-than-four-million?. Это простая проверка того, что параметр функции меньше 4 миллионов. Разбор следующего выражения лучше начать с самых внутренних скобок. Сначала мы получаем бесконечную последовательность чисел Фибоначчи. Затем мы используем функцию take-while. Это аналог функции take, который в качестве аргумента использует не конкретное число, а условное выражение. Функция take-while берет элементы из последовательности до тех пор, пока условие не становится ложным. Таким образом, как только число из последовательности Фибоначчи становится больше 4 миллионов, мы перестаем вычислять элементы последовательности. Далее полученную последовательность мы передаем функции filter. В качестве фильтра используется встроенная функция even? , которая делает именно то, что вы подумали: проверяет, является ли число четным. Результирующая последовательность содержит четные числа Фибоначчи, меньшие 4 миллионов. Затем мы суммируем все эти числа, используя функцию reduce, так же, как мы это делали в предыдущем примере.

Код, приведенный в листинге 7, решает поставленную задачу, но он еще далек от совершенства. Для использования функции take-while нам пришлось определить совсем простую функциюless-than-four-million?. Однако делать это было совсем не обязательно. Из самого названия языка Clojure понятно, что он поддерживает замыкания (closure). Упрощенный код показан в листинге 8.


Замыкания в Clojure

Замыкания часто используются во многих языках программирования, особенно в функциональных языках, таких как Clojure. Помимо того, что функции являются основными элементами программы и могут передаваться в качестве аргументов другим функциям, они еще могут быть встроенными или анонимными. Листинг 8 демонстрирует упрощенный код листинга 7 с использованием замыкания.

Листинг 8. Упрощенный код
(println (reduce + 
    (filter even? 
        (take-while (fn [n] (< n 4000000)) (lazy-seq-fibo)))))

В листинге 8 мы используем макрос fn. Он создает анонимную функцию и возвращает ее значение. Условные функции, как правило, очень просты, и поэтому их лучше определять с использованием замыкания. Как мы увидим далее, Clojure предоставляет очень удобный способ записи замыканий.

Листинг 9. Краткая запись замыкания
(println (reduce + 
    (filter even? 
        (take-while #(< % 4000000) (lazy-seq-fibo)))))

В листинге 9 для создания замыкания вместо макроса fn мы воспользовались #. Кроме того, мы использовали символ % для обозначения первого параметра функции. Вы также можете использовать %1 для первого параметра и %2, %3 и т.д. в случае, если функция принимает несколько параметров.

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


Пример 3: Использование Java-технологий

Платформа Java обладает целым рядом неоспоримых преимуществ. Основными ее достоинствами являются высокая производительность JVM и богатейший набор функций, поставляемых как в основных API, так и в виде библиотек, созданных независимыми разработчиками на Java. Таким образом, Java предоставляет вам возможность использовать готовые функции вместо того, чтобы многократно заново изобретать велосипед. Clojure был создан с учетом возможности использования Java. Вы можете вызвать Java-методы, создавать Java-объекты, использовать Java-интерфейсы и расширения Java-классов. Для наглядной демонстрации возможностей использования Java решим еще одну задачу из коллекции Project Euler.

Листинг 10. Задача №8 из коллекции Project Euler
Найти наибольшее произведение пяти последовательных цифр в записи 1000-знакового числа.

73167176531330624919225119674426574742355349194934 
96983520312774506326239578318016984801869478851843 
85861560789112949495459501737958331952853208805511 
12540698747158523863050715693290963295227443043557 
66896648950445244523161731856403098711121722383113 
62229893423380308135336276614282806444486645238749 
30358907296290491560440772390713810515859307960866 
70172427121883998797908792274921901699720888093776 
65727333001053367881220235421809751254540594752243 
52584907711670556013604839586446706324415722155397 
53697817977846174064955149290862569321978468622482 
83972241375657056057490261407972968652414535100474 
82166370484403199890008895243450658541227588666881 
16427171479924442928230863465674813919123162824586 
17866458359124566529476545682848912883142607690042 
24219022671055626321111109370544217506941658960408 
07198403850962455444362981230987879927244284909188 
84580156166097919133875499200524063689912560717606 
05886116467109405077541002256983155200055935729725 
71636269561882670428252483600823257530420752963450

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

Листинг 11. Разбор текста
(def big-num-str 
    (str "73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450"))

Здесь мы воспользовались реализацией многострочных текстовых переменных в Clojure. Функция str выполняет разбор многострочной текстовой константы. Затем мы используем макрос def для определения текстовой константы big-num-str. Однако, для решения нашей задачи самое удобное – это преобразовать эту константу в последовательность целых чисел. Это преобразование показано в листинге 12.

Листинг 12. Создание последовательности целых чисел
(def the-digits
    (map #(Integer. (str %)) 
        (filter #(Character/isDigit %) (seq big-num-str))))

Вновь начнем разбор кода с самых внутренних скобок. Функция seq используется для преобразования big-num-str в последовательность. Однако оказывается, что эта последовательность – не совсем то, что нам нужно. С помощью REPL вы и сами легко можете в этом убедиться:

Листинг 13. Проверка последовательности big-num-str
user=> (seq big-num-str)
(\7 \3 \1 \6 \7 \1 \7 \6 \5 \3 \1 \3 \3 \0 \6 \2 \4 \9 \1 \9 \2 \2 \5 \1 \1 \9
 \6 \7 \4 \4 \2 \6 \5 \7 \4 \7 \4 \2 \3 \5 \5 \3 \4 \9 \1 \9 \4 \9 \3 \4
 \newline...

REPL показывает цифры в записи числа (Java char) как \c. Таким образом, цифре 7 в записи числа соответствует элемент \7, а сочетанию \n (переход на новую строку) соответствует элемент \newline. Именно такую последовательность мы и получим при непосредственном преобразовании текста. Таким образом, прежде чем приступать к каким-либо вычислениям, нам надо избавиться от элементов, соответствующих переходу на новую строку, и преобразовать цифры в целые числа. Эти дополнительные действия приведены в листинге 12. Чтобы избавиться от символов новой строки, мы накладываем фильтр. Обратите внимание, что в коде опять используется краткая запись условной функции, передаваемой filter в качестве аргумента. Функция-замыкание использует Character/isDigit. Это static-метод isDigit из класса java.lang.Character. Таким образом, наш фильтр пропустит цифры и отфильтрует символы новой строки.

Теперь, когда мы избавились от символов новой строки, можно приступать к преобразованию цифр в целые числа. Рассмотрим следующий уровень вложенности фрагмента, приведенного в листинге 12. Здесь используется функция map с двумя параметрами: функцией-аргументом и последовательностью map возвращает новую последовательность, n-ный элемент которой есть результат применения функции-аргумента к n-ному члену исходной последовательности. В качестве функции-аргумента мы вновь используем краткую запись замыкания. Сначала мы вызываем функцию Clojure str для преобразования символа в строку. Зачем нам это? - спросите вы. Затем, что потом мы преобразуем эту строку в целое число, используя конструктор для java.lang.Integer. В листинге этому соответствует Integer. Вы можете рассматривать это выражение как java.lang.Integer(str(%)) . Объединив такое преобразование с функцией map, мы получаем необходимую последовательность целых чисел. Теперь мы можем решить исходную задачу.

Листинг 14. Задача 3
(println (apply max 
    (map #(reduce * %)
        (for [idx (range (count the-digits))] 
            (take 5 (drop idx the-digits))))))

Чтобы разобраться в этом фрагменте кода, начнем с макроса for. Этот не Java-цикл for, это последовательное включение. Сначала мы определяем привязку, используя квадратные скобки. В нашем примере переменная idx привязана к последовательности 0… N-1, где N – количество элементов в последовательности the-digits (N=1000, так как в исходном числе 1000 знаков). Затем в макросе for определяется выражение, по которому будет создаваться новая последовательность. Для каждого элемента в последовательности idx макрос вычислит заданное выражение и добавит результат к новой последовательности. Как можно заметить, в определенном смысле макрос for работает аналогично циклу for. Выражение, используемое для вычисления элементов новой последовательности, сначала обращается к функции drop для того, чтобы отбросить первые M элементов последовательности, а затем вызывает функцию take для того чтобы отобрать первые пять элементов из оставшейся последовательности. Следует помнить, что M будет принимать значения 0, 1, 2, и т.д., так что результатом работы будет последовательность наборов по пять чисел в каждом. Первый элемент будет иметь вид (e1, e2, e3, e4, e5), второй элемент будет иметь вид (e2, e3, e4, e5, e6), и т.д., где e1, e2, и т.д. - элементы the-digits.

Теперь применим функцию map к набору последовательностей из 5 элементов. Функция-аргумент reduce преобразует последовательность из 5 элементов в произведение этих элементов, так что на выходе функции мы получим последовательность целых чисел, первое из которых является произведением элементов 1-5, второе – произведением элементов 2-6, и т.п. Нам нужно найти наибольшее из таких произведений. Для этого используется функция max. Однако она работает с несколькими аргументами, а не с одной последовательностью. Для преобразования последовательности в набор параметров, используется функция apply. Таким образом, мы находим требуемый максимум и выводим его на печать. Мы с вами разобрали несколько задач и написали несколько программ на Clojure для их решения, одновременно потренировавшись в применении языка.


Заключение

В этой статье мы познакомили вас с языком программирования Clojure и продемонстрировали возможности плагина Clojure для Eclipse. Мы кратко коснулись особенностей философии и реализации языка, однако основное внимание статьи было сконцентрировано на разборе конкретных примеров кода. На этих простых примерах мы постарались продемонстрировать основные структуры языка: функции, макросы, привязки, рекурсии, «ленивые» последовательности, замыкания, включения и интеграцию с Java-технологией. К сожалению, в рамках одной статьи невозможно рассмотреть все аспекты использования Clojure. Однако мы надеемся, что нам удалось привлечь ваше внимание к возможностям Clojure, и вы постараетесь узнать о нем больше.


Загрузка

ОписаниеИмяРазмер
Код, используемый в данной статьеos-eclipse-clojure-euler.zip2Кб

Ресурсы

Научиться

  • Ознакомьтесь с оригиналом статьи (EN).
  • На сайте Clojure.org (EN) вы можете загрузить Clojure, ознакомиться с учебным руководством и найти справочную литературу.
  • Ознакомьтесь с полным списком математических задач, опубликованных на сайте Project Euler (EN).
  • Ресурс clojure-contrib (EN) предоставляет обновления и исходные коды для основных библиотек, разработанных сообществом Clojure и используемых в большинстве Clojure-проектов. Библиотека clojure.contrib по умолчанию включена в Clojure-плагин для Eclipse.
  • Самый лучший способ повысить свой уровень от начинающего программиста на Clojure до Clojure-эксперта – прочесть книгу Стюарта Хэлловея Programming Clojure (EN). Пример последовательности чисел Фибоначчи мы взяли именно из этой книги.
  • Ознакомьтесь с принципами работы Clojure и перспективами его развития, посмотрев интервью с создателем Clojure Ричем Хикеем (Rich Hickey) (EN).
  • Прочтите "Beginning Haskell" (EN) для знакомства с еще одним функциональным языком программирования.
  • Статья о функциях высшего порядка "Higher-order functions" (EN) поможет вам лучше понять принципы использования таких функций и познакомит с языком Scheme, который также является диалектом Lisp.
  • Материалы публикации "Develop Lisp applications using the Cusp Eclipse plug-in" (EN) помогут вам узнать, как создавать программы на Lisp в среде разработки Eclipse.
  • Статья "The busy Java developer's guide to Scala: Functional programming for the object oriented" рассматривает реализацию аспектов функционального программирования на JVM в языке программирования Scala.
  • Начинающим пользователям Eclipse рекомендуем ознакомиться со статьей "Getting started with the Eclipse Platform" (EN).
  • Прослушать интересные интервью и дискуссии для разработчиков ПО можно с помощью подкастов ресурса developerWorks (EN).
  • Чтобы быть в курсе последних новостей, посещайте сайт технических мероприятий и Web-трансляций (EN) developerWorks.
  • Приглашаем вас стать последователем developerWorks на Twitter (EN).
  • Ознакомьтесь с предстоящими конференциями, демонстрациями, Web-трансляциями и прочими мероприятиями (EN) во всём мире, представляющими интерес для разработчиков ПО с открытым исходным кодом на базе продуктов IBM.
  • В разделе Open Source (EN) сайта developerWorks приведено множество практических инструкций, инструментов и новостей, которые помогут вам применять открытые технологии в своих разработках и использовать их с продуктами IBM.
  • Демо-ролики developerWorks (EN) помогут вам подробнее познакомиться с преимуществами открытых технологий, а также с технологиями и продуктами IBM.

Получить продукты и технологии

  • Вам потребуется Java Development Kit (EN) версии 5 или выше. При написании примеров, рассматриваемых в данной статье, использовалась версия Java Development Kit 1.6.0_13.
  • Загрузите Clojure V1 (EN).
  • Скачайте последнюю версию Eclipse IDE (EN). Материал данной статьи основывается на использовании Eclipse V3.5.
  • Ресурс clojure-dev (EN) предоставляет среду разработки на Clojure, созданную на платформе Eclipse. При подготовке статьи использовалась версия V0.0.34.
  • Реализуйте инновации в вашем следующем проекте разработки с открытым кодом с помощью ознакомительного ПО IBM (EN), которое можно загрузить с сайта или заказать на DVD.
  • Download Ознакомительные версии продуктов IBM (EN) и практические занятия в "песочнице" IBM SOA (EN) помогут вам освоить инструменты разработки приложений и промежуточное ПО семейств DB2®, Lotus®, Rational®, Tivoli® и WebSphere®.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


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

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=458548
ArticleTitle=Язык программирования Clojure
publish-date=12232009