Комментарии 53
++[[]][+[]]+[+[]] === "10"
true
['10', '10', '10'] .map(parseInt)
[ 10, NaN, 2 ]
['10', '10', '10'].map(parseInt)
Ответ[10, NaN, 2]
Ожидаемое поведение. Всё согласно спецификации.
Синтаксис функции Array.prototype.map():
arr.map(callback, thisArg);
Параметры:
- callback — функция, создающая элемент в новом массиве, принимает три аргумента:
- currentValue — текущий обрабатываемый элемент массива
- index — индекс текущего обрабатываемого элемента в массиве
- array — массив, по которому осуществляется проход
- thisArg — значение, используемое в качестве this при вызове функции callback, необязательный параметр
Синтаксис функции parseInt():
parseInt(string, radix);
Параметры:
- string — значение, которое необходимо проинтерпретировать
- radix — целое число в диапазоне между 2 и 36, представляющее собой основание системы счисления числовой строки string
В вашем примере функция parseInt() будет вызвана три раза со следующими аргументами:
parseInt('10', 0, [10, 10, 10]); // 10
parseInt('10', 1, [10, 10, 10]); // NaN
parseInt('10', 2, [10, 10, 10]); // 2
Статья очень хорошо переварена для неподготовленного читателя(статья на викиконспектах зашла далеко не с первого раза)
На самом деле неплохо было бы раскрыть понятия: каррирования/декаррирования и частичного применения функций, которые тут показаны, но не указано, что это они и есть, а прочитавшие статью могут так и не понять, что страшные термины, которых они ранее избегали весьма безобидные и понятные.
https://www.npmjs.com/package/inet-lib
Почему в лисп?
Ну согласитесь, практически один в один!
Что касается комбинаторной логики — основные комбинаторы описать не сложно, но я бы с удовольствием глянул на реализацию того же алгоритма Евклида в подобном стиле.
Ну комбинаторы были предшественниками лямбда исчислению. Их идея в том что у них есть только одна метаоперация в отличии от лямбда исчисления где их две (абстракция и аппликация) Вот вам задание напишите дерево на js потом на Черче и попробуйте написать изоморфны функции чтобы через обычное дерево считать для Черча (у меня все это написано на хаскель через ана и катаморфизмы используя F-algebra) На js я вроде тоже подобное видел называется excursion (от DrBoolean) Но как было замечено что лучше использовать языки типа хаскель для таких забав которыми вы тут занимались =)
Увы, haskell подходит только для типизированного лямбда-исчисления, я пробовал. Именно поэтому пришлось прибегнуть к языку с динамической типизацией.
Если я говорю, что можно использовать Черча в хаскель то это так и есть. И выглядит даже лучше
Продемонстрируйте, пожалуйста, операцию вычитания для чисел Чёрча на хаскеле. Вероятно, я просто не смог её написать, поэтому хочу взглянуть, как это делается.
Позже, нет доступа к компьютеру сейчас
gen.lib.rus.ec/search.php?req=методы+рекурсивного+программирования&lg_topic=libgen&open=0&view=simple&res=25&phrase=1&column=def
Это M комбинатор, в энергичных языках его определить можно, а в ленивых типа хаскеля нет (это если все что у ас есть то примитивы как функции)
В лиспе только простейшие операции над списками типа «первый элемент», «добавить элемент к началу», «следующие за первым элементы» и т.п. Рекурсивный вызов и лямбды есть, но они имеет явно не математическое обоснование.
Можно немного конкретнее, чем лямбда-исчисление ближе к Хасеклю, чем к Лиспу?
Мне кажется, что система типов в этой ситуации решает далеко не в сторону Хаскеля.
Ни арифметики Черча, ни «все есть функция», ничего подобного.
Просто есть ячейка «значение + следующий элемент», и на основании таких ячеек строится язык.
Википедия конечно может тоже правду пишет, на все можно посмотреть с разных сторон. И на Луне можно сыр найти.
Вот еще интересная ссылка: http://norvig.com/lispy.html реализация Лиспа на Питоне, около 40-а строк. Математикой и не пахнет, чистая работа с данными.
А Хаскель как раз вокруг математики Чарча и построен, именно из-за этой математики у всех при изучении Хаскеля такая головная боль.
Пол Грехм, «On Lisp»
Интересно видеть символ лямбды на титульной странице! И почему вас не удивляет, что в Лиспе все функции префиксные (в отличие от того же Хаскеля). Так или иначе — в книге нет ни слова о истории создания Лиспа. Да и числа Чёрча там, естественно, не будут использоваться, ибо машинное представление эффективнее на практике.
ячейка «значение + следующий элемент»
Есть так же другая уважаемая книга:
Гарольд Абельсон и Джералд Джей Сассман, "Структура и интерпретация компьютерных программ".
Советую взглянуть на параграф 2.1.3, там как раз про cons
, car
и cdr
.
А Хаскель как раз вокруг математики Чарча и построен
В первую очередь он построен вокруг идеи функционального программирования, появившейся с Лиспом, и вокруг системы типов Хиндли-Милнера (да, она имеет корни в математике, но головной боли ни у кого не вызывает).
Лисп: все есть список
Хаскель: все есть функция
Мне как-бы все очевидно.
Вы ссылку мне дали и там черным по белому написано: «Демонстрировать процедурную реализацию имеет смысл не для того, чтобы показать, как работает наш язык (Scheme, и вообще Лисп-системы, реализуют пары напрямую из соображений эффективности), а в том, чтобы показать, что он мог бы работать и так. »
То есть лисп работает на реальных списках, а не на функциях. Списки можно выразить при помощи функций, равно как и функции можно представить в виде списков. Но то что car и cdr являются одними из (шести кажется) форм, которые принципиально нельзя выразить другими операциями, как-бы намекает.
Это как если ты используешь счеты, и тут приходит перец и говорит: смотри, я выразил твои исчисления при помощи матанализа, вот книга которую ты должен изучить чтобы научиться считать на счетах, и ты должен меня уважать, так как я придумал счеты, я математик. Че??
Хиндли-Милнер как раз очевиден и не вызывает головной боли, если не пытаться его читать в оригинале.
Я о математике выражения всего через функции говорил. Про вот эту запись: «L3 = f => x => f (a0) (f (a1) (f (a2) (x)))» для вас может это и очевидно, а для обычных людей это белиберда. Когда математики начинают разжевывать, оказывается что они просто все запутали излишним использованием символизации в месте где можно было использовать обычную человеческую речь, а сама идея и выеденного яйца не стоит.
Меня возмущает то, как легко вы плюёте на математику, лежащую в основе программирования.
Цель статьи — показать, насколько лямбда-исчисление, которому без малого 100 лет, по факту близко к современному программированию. Что это не просто бесполезная заумная теория, какой её себе представляют люди подобные вам.
Если вы не хотите задумываться над тем, что читаете, то данная статья не для вас. У меня отбито всё желание продолжать данный диалог, извините.
Ну если не хотите продолжать диалог, то жалко. Статья на самом деле мне понравилась, и выражение примитивных операций при помощи функций — довольно интересная для меня тема.
Для получения предыдущего числа мы строим пару
Который раз читаю про это и всегда удивляюсь. Почему для сложения есть простое решение, а для вычитания сложное и на сложение непохожее? Неужели никто не обращает внимание, что это выглядит как костыль?
Для их построения, по сути, нужны только ноль и функция +1
Если мы вводим функцию +1, почему нельзя ввести функцию -1?
Как следствие, легко заметить, что число, предыдущее для нуля, тоже будет нулём. Это спасает от неопределённости и даёт множество других преимуществ.
Какие тут преимущества, если банальную параболу нельзя построить? Кстати, а что насчет дробных чисел?
Операция "+1" вводится для натуральных чисел аксиоматически, а вот для реализации "-1" нужно перебирать числа, начиная с нуля, пока не найдём подходящее. Так уж числа устроены. Введение "-1" в аксиоматику избыточно и только бы всё усложнило.
Какие тут преимущества, если банальную параболу нельзя построить?
Не совсем понимаю, причём тут парабола. Кодировались неотрицательные целые числа. Если нужны числа со знаком, можно и для них структуру данных создать, например пара (знак, модуль)
. Дроби тоже кодируются парой (числитель, знаменатель)
.
Как было сказано в начале статьи, всегда в распоряжении есть булевы значения и кортежи любой длины, хоть IEEE 754 реализуй, никаких ограничений!
Мы же не перебираем все числа до миллиона при вычислении выражения 1000000 — 1. Мы пользуемся правилами вычисления.
В примере с натуральными числами они не были реализованы через кортеж битов. Потому и возник вопрос про аналогичное построение отрицательных а заодно и дробных чисел. Потому что никаких преимуществ в отсутствии отрицательных чисел нет.
Преимущества есть в том, что перед нулём находится ноль, это позволяет легко определить операции сравнения. Реализация Lte
, например, именно этим фактом и пользуется.
Касательно 1000000-1 — в теории (в арифметике Пеано) вычисление происходит именно перебором чисел от 0 до 999999. На практике же мы используем более продвинутые инструменты.
А, то есть если мне понадобятся отрицательные числа, то у меня еще и функция Lte сломается)
Как я понял, в арифметике Пеано и нет отрицательных чисел, она только про возрастающие положительные. Но если идет разговор о красоте и мощности функционального исчисления, значит в нем должны быть средства реализовать такие простые вещи.
Lte рассчитана на целые неотрицательные числа, для сравнения знаковых целых понадобится другая функция.
значит в нем должны быть средства реализовать такие простые вещи
Средства есть, числа со знаком реализуются в виде пар. Неплохое объяснение есть в википедии: https://en.wikipedia.org/wiki/Church_encoding#Signed_numbers
Вам стоит понять, что, как и в школьной арифметике, в первую очередь вводятся натуральные числа, а уже потом на их основании целые и рациональные. И это вовсе не говорит о каких-то проблемах в лямбда-исчислении.
Ок. То есть, я правильно понял, там есть отрицательные числа, но в операциях с ними все равно поиск предыдущего используется?
Но ведь, если ввести -1, то все вычисления станут проще, разве нет?
Числа изначально вводятся через понятие количества, или длины цепочки вызовов. Это наиболее естественный способ, не подразумевающий под собой наличие константы "-1". Те же числа потом и используются похожим образом — индекс в списке, длина списка, длина подпоследовательности… Наличие знака при этом было бы избыточным и ничего бы не упростило.
Вы верно поняли, описанное представление чисел со знаком всё равно требует трудоёмких операций поиска соседнего числа. Я представляю себе только один способ избавиться от этого — кодировать числа в позиционной системе счисления. Но подобная тема по своему объёму сама заслуживает отдельной статьи.
К вопросу о том, станут ли вычисления проще — если вычисления требуют отрицательных чисел, то мы можем и должны их предоставить. Не знаю, что тут ещё можно добавить.
Почему избыточным, если они нам нужны и мы все равно вводим их через пары? Я бы даже сказал, имитируем. Причем вводим при этом две дополнительные константы для знаков вместо одной операции.
Я не столько про алгоритмическую сложность, сколько про сам подход. Сложение сделано так, а обратная операция абсолютно по-другому. Нелогично.
Вот я и говорю, зачем нам только натуральные. Можно же и дополнительные множества определить, и вычитание нормально сделать. Неужели в функциональном программировании нет такого способа?
Лямбда-исчисление на JavaScript