Да.
Вы можете реализовать любой алгоритм с картой, уменьшением и фильтрацией в контексте современных языков, которые реализуют эти конструкции. Предикатные функции, на которые они полагаются, являются полными по Тьюрингу, что делает их полными по Тьюрингу.
Но это умный ответ **, не так ли? Реализация всего вашего алгоритма в функции предиката и оборачивание его в вызов карты кажется обманом.
Можете ли вы реализовать алгоритм только с картой, уменьшить и отфильтровать?
Нет.
Эти три функции сами по себе не могут выполнять приседания. Написание чего-то подобного map(reduce(reduce(map(reduce(filter(4))))))
ничего не сделает.
Сначала мы должны решить, что мы пытаемся спросить.
Я терял сон из-за всего этого с тех пор, как на прошлой неделе меня обстреливал технический редактор. «Умный человек однажды сказал мне, что вы можете написать любую программу в виде последовательности карт и уменьшить количество вызовов. Я хотел бы добавить фильтр для более полного набора инструментов,» написал я в визуализации данных с d3.js .
Единственный комментарий редактора был: «Это неправильно. Например, вы не можете реализовать такую сортировку ».
И да, он прав. Но он также не. Что Smart Guy ™ пытался рассказать мне все те годы назад, когда он познакомил меня с функциональным программированием?
Вернуться к основам.
Чтобы иметь полный язык Тьюринга, вам нужно:
- Напишите ячейку
- Читать ячейку
- Двигай влево
- Переместить вправо
- Ветвление (основано на значении ячейки)
Мы предполагаем, что наша память практически бесконечна. Это означает, что это не бесконечно само по себе, но мы можем добавить больше, когда закончится.
В своей работе по созданию LISP Маккарти писал, что рекурсивной альтернативе итеративной машине Тьюринга нужны следующие конструкции:
atom
— является ли этот символ атомом (одним из основных символов)eq
— оба эти символы атомыcar
— возвращает первую часть двухэлементного спискаcdr
— возвращает вторую часть двухэлементного спискаcons
— строит список из двух элементов- рекурсивные определения с использованием этих пяти конструкций
Куда это приведет нас к нашему первоначальному вопросу? Можем ли мы построить что-нибудь из последовательности карт, уменьшить и отфильтровать?
Это зависит.
В контексте строгого функционального языка , с добавлением рекурсии, да, мы, вероятно, можем.
В конце концов, внедрение наивной быстрой сортировки в Haskell занимает не более , чем рекурсии, car
, cdr
, cons
и фильтр.
quicksort [] = [] quicksort (x:xs) = let smaller = quicksort (filter (<=x) xs) bigger = quicksort (filter (>x) xs) in smaller ++ [x] ++ bigger
В основном, если quicksort
получен пустой список, вернуть пустой список. В противном случае создайте новый список и поместите элементы меньше, чем x
слева, больше справа. ++
является по существу минусом, и этот (x:xs)
бит разделяет список, поэтому первый элемент становится, x
а остальные элементы становятся xs
.
Это одна из самых кратких реализаций сортировки, которую я когда-либо видел. И мы не на самом деле выйти из нашей карты, уменьшить и рамку фильтра, сделали мы? Возможность добавления и разделения списков обычно предполагается.
Но можем ли мы пойти еще дальше? Можем ли мы реализовать filter
с помощью карты и уменьшить? Давай попробуем.
function filter (predicate, list) { return list.map(function (a) { return predicate(a) ? [a] : null; }).reduce(function (prev, current) { return current ? prev.concat(current) : prev; }, []); }
Да мы можем. Даже в Javascript, о котором был первоначальный вопрос. Это довольно просто: пометьте нежелательные записи с помощью map
, а затем используйте reduce
для создания нового списка без нежелательных записей.
Мы могли бы пойти дальше и использовать только reduce
?
function filter2 (predicate, list) { return list.reduce(function (prev, current) { return predicate(current) ? prev.concat(current) : prev; }, []); }
Получается, что reduce
и filter
это одна и та же функция, если предположить, что разветвление и сравнение являются заданными. .concat
кстати, есть минусы Javascript.
Мы можем реализовать этот пример на Haskell в Javascript . Доказательство того, что даже с Javascript, рекурсии, сопоставления и сокращения достаточно, чтобы сделать что-нибудь.
function quicksort (list) { if (list.length == 0) { return []; } var x = list[0], xs = list.slice(1); return quicksort(filter2(function (a) { return a <= x; }, xs)) .concat(x) .concat( quicksort(filter2(function (a) { return a > x; }, xs))); }
Точно такой же код, как и раньше, просто более словарный, потому что Javascript более пушистый, чем Haskell.
Но ближе ли мы к открытию нашего вопроса?
Не на самом деле нет. Мы только что показали, что с учетом условий, способа нарезки и добавления списков, рекурсии, сопоставления и сокращения мы можем делать все что угодно. Даже в Javascript.
Вы должны прочитать мою книгу, почему программисты работают по ночам .