Рубрика «функциональное программирование» - 41

Статья будет состоять из 3х частей:
Пальчиковые деревья (часть 1. Представление)
Пальчиковые деревья (часть 2. Операции)
Пальчиковые деревья (часть 3. Применение)

Пальчиковые Деревья как Последовательности

Пальчиковые деревья (часть 2. Операции) - 1
В первой части статьи мы рассмотрели пальчиковые деревья как перспективную структуру в качестве немутабельных последовательностей. И научились создавать пальчиковые деревья. Хочу заметить, научились создавать так, что стало принципиально невозможно построить неправильные деревья. Теперь наша задача научится работать с пальчиковыми деревьями как с последовательностями: научится присоединять к началу и концу последовательности, научится легко отделять от обоих концов последовательности, а также соединять несколько деревьев в одно.
Читать полностью »

Pipe matching в ЯП Clojure (метапрограммирование в Lisp для начинающих) - 1

Введение

Несколько дней назад я открыл для себя замечательный ЯП Clojure — один из современных диалектов Lisp, особенностью которого является хорошая реализация средств многопоточности, компиляция в байткод jvm, соответственно возможность использования java — библиотек, jit-компиляция и т.д. Про Clojure можно почитать например тут. Но в этой статье речь пойдёт о метапрограммировании. Lisp устроен таким образом, что данные и код в нём — одно и то же. Объявления функций, макросов, вызовы функций, развёртывание макросов — в Lisp это всё просто списки, возможно вложенные друг в друга.

(defn square [foo] (* foo foo))
(defmacro show-it [foo] `(println ~foo))

Такое единство кода и данных предоставляет мощные возможности для метапрограммирования — код который пишет код, который пишет код, который пишет код и т.д. — это самое обычное дело для программирования на Lisp. В compile-time нам полностью доступен весь функционал языка, мы можем вызывать функции, развёртывать макросы, возможно рекурсивно. Например, если мы определим вот такой макрос:
Читать полностью »

Вышла недавно статья на Хабре о том, как можно самому создать на функциональном языке такие структуры как Очередь (первый зашёл, первый вышел) и Последовательность (напоминает двусторонний стек — первый зашёл, первый вышел с обоих концов). Посмотрел я на этот код и понял, что он жутко неэффективен — сложность порядка O(n). Быстро сообразить, как создать структуры с O(1) у меня не вышло, поэтому я открыл код библиотечной реализации. Но там была не лёгкая и понятная реализация, а <много кода>. Это было описание пальчиковых деревьев, необходимость и элегантность которых для этой структуры данных хорошо раскрывается текущей статьёй.

Пальчиковые деревья

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

Статья будет состоять из 3х частей:
Пальчиковые деревья (часть 1. Представление)
Пальчиковые деревья (часть 2. Операции)
Пальчиковые деревья (часть 3. Применение)

Разрабатывая структуру данных

Основа и мотивация пальчиковых деревьев пришла от 2-3 деревьев. 2-3 деревья — это деревья, которые могут иметь две или три ветви в каждой внутренней вершине и которые имеют все свои листья на одном и том же уровне. В то время, как бинарное дерево одинаковой глубины d должны быть 2d листьев, 2-3 деревья гораздо более гибкие, и могут быть использованы для хранения любого числа элементов (количество не должно быть степенью двойки).
Рассмотрим следующее 2-3 дерево:
Пальчиковые деревья (часть 1. Представление) - 1
Это дерево хранит четырнадцать элементов. Доступ к любому из них требует трех шагов, и если бы мы должны были добавить больше элементов, количество шагов для каждого из них будет расти логарифмически. Мы хотели бы использовать эти деревья для моделирования последовательности. Тем не менее, во многих применимых последовательностях очень часто и неоднократно обращаются к началу или к концу, и гораздо реже к середине. Для удовлетворения этого пожелания, мы можем изменить эту структуру данных так, чтобы приоритет доступа к началу и к концу был наивысшим в отличие от других особенностей.
В нашем случае, мы добавляем два пальца. Палец просто точка, в которой вы можете получить доступ части структуры данных, в императивных языках это было бы просто указателем. В нашем случае, однако, мы будем реструктуризовать всё дерево и сделаем родителей первых и последних детей двумя корнями нашего дерева. Визуально, рассматривая вопрос об изменении дерева выше, захватываем первый и последний узлы на предпоследнем слое, и тянем их вверх, позволяя остальной части дерева свисать:
Пальчиковые деревья (часть 1. Представление) - 2
Читать полностью »

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

MailboxProcessor

image

Читать полностью »

6-7 ноября 2014 года в Перми будет проведен конкурс «Открытый регион. Хакатон» по разработке приложений и сервисов на основе открытых данных Пермского края.

На сайте opendata.permkrai.ru опубликовано примерно 1400 статистических показателей по различным областям жизнедеятельности края. Что можно сделать с этими данными? Первая мысль, которая пришла мне в голову, — создать аналог сайта Spurious Correlations (ложные корреляции).

TL; DR:
Исходники: github.com/yakov-bakhmatov/odpr
Приложение: odpr.bakhmatov.ru/
Читать полностью »

Язык Julia не поддерживает такую технику программирования, хорошо зарекомендовавшую себя в языках Haskell, Prolog, Erlang, Scala, Mathematica, как pattern matching. Но разрешает писать макросы, которые позволяют исправить этот фатальный недостаток. Выглядит это примерно так:

julia> immutable X a end

julia> immutable Y a ; b end

julia> @case(Y(X(9),2),  Y(4,3)-> 55, Y(X(k),2)->1+k)
10

Исходный код доступен на github.
Похожую (но гораздо более развитую и готовую для использования) можно взять здесь, но она слишком большая, что бы разбирать ее как пример в статье.
Читать полностью »

Идея для Clojure Cup этого года родилась примерно вот здесь:

Как мы писали Clojure Cup в этом году: The Hat
(город Вырица, Ленобласть)

Ожидая обратную электричку мы убивали время, как могли, и грустили, что под рукой нет чего-то вроде Alias'а или Шляпы — всё же салонные игры это интеллигентнее, чем косплей гопоты в заброшке. TL;DR:

Как мы писали Clojure Cup в этом году: The Hat

Играть: http://playthehat.com (ещё с багами и не работает не на хроме, мы не можем править до окончания голосования судей)
Голосовать: https://clojurecup.com/#/apps/thehat

Более длинная версия поста — ниже.
Читать полностью »

Оригинальная статья — Understanding Monads With JavaScript (Ionuț G. Stan).
Буду признателен за комментарии об ошибках/опечатках/неточностях перевода в личку

От автора

Последние несколько недель я пытаюсь понять монады. Я все еще изучаю Haskell, и, честно говоря, думал, что знаю, что это такое, но когда я захотел написать маленькую библиотечку — так, для тренировки — я обнаружил, что хотя и понимаю, как работают монадические bind (>>=) и return, но не представляю, откуда берется состояние. Так что, вероятно, я вообще не понимаю, как это все работает. В результате, я решил заново изучить монады на примере Javascript. План был тот же, когда я выводил Y Combinator: взял изначальную задачу (здесь это взаимодействие с неизменяемым явно состоянием), и проделал весь путь к решению, шаг за шагом изменяя изначальный код.
Читать полностью »

Всем привет!

Увидел, что статья о трансдьюсерах на JavaScript стала вполне популярной и хотел отметить, что уже давно доступен генераторов парсеров на транзисторах^W трансдьюсерах. По крайней мере, очень на это похоже. У меня есть статья с подробным описанием на английском «Generating Functional Parsers» и, собственно, исходники.

Тут какие-то новые правила, постов-ссылок уже нет, просят всё обильно описывать — так что я подчинюсь и парой слов (разбавленных необходимой водой), расскажу, о чём это, а то могу ведь и не прав оказаться. Тем более русской версии статьи пока нет, да и скорее всего не будет. Одна надежда, что эта пройдёт.
Читать полностью »

В первой части мы остановились на следующей спецификации: Трансдьюсер — это функция принимающая функцию step, и возвращающая новую функцию step.

step⁰ → step¹

Функция step, в свою очередь, принимает текущий результат и следующий элемент, и должна вернуть новый текущий результат. При этом тип данных текущего результата не уточняется.

result⁰, item → result¹

Чтобы получить новый текущий результат в функции step¹, нужно вызвать функцию step⁰, передав в нее старый текущий результат и новое значение, которое мы хотим добавить. Если мы не хотим добавлять значение, то просто возвращем старый результат. Если хотим добавить одно значение, то вызываем step⁰, и то что он вернет возвращаем как новый результат. Если хотим добавить несколько значений, то вызываем step⁰ несколько раз по цепочке, это проще показать на примере реализации трансдьюсера flatten:

function flatten() {
  return function(step) {
    return function(result, item) {
      for (var i = 0; i < item.length; i++) {
        result = step(result, item[i]);
      }
      return result;
    }
  }
}

var flattenT = flatten();

_.reduce([[1, 2], [], [3]], flattenT(append), []); // => [1, 2, 3]

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

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

Итак, сейчас мы можем:

  1. Изменять элементы (прим. map)
  2. Пропускать элементы (прим. filter)
  3. Выдавать для одного элемента несколько новых (прим. flatten)

Читать полностью »


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js