Как стать автором
Обновить

Что вы знаете о символьном программировании?

Уровень сложностиСредний
Время на прочтение15 мин
Количество просмотров17K
Всего голосов 40: ↑40 и ↓0+40
Комментарии98

Комментарии 98

Спасибо за статью. Есть ли реализации символьного программирования в других популярных языках Java, c++, c# в виде библиотек?

В .net есть Expression, позволяющие работать с деревьями выражений. Например https://habr.com/ru/articles/149630/

Концепцию символьного программирования лучше начинать изучать через нормальные алгоритмы Маркова, и ещё лучше - самому написать для них интерпретатор (это просто).

К своему стыду во время написания статьи я не вспомнил про алгоритмы Маркова. Вы абсолютно правы на счет того, что это лучший теоретический пример символьного программирования. Но хочу сказать, клеточные автоматы, которыми вдохновлялся Стивен Хьюгович и алгоритмы Маркова это практически одно и тоже. Только автоматы чуть-чуть более примитивные. На самом деле всю концепцию исполнения кода в WL точно так же можно свести к нормальным алгоритмам Маркова, чего я к сожалению в статье не показал, но если я найду в себе силы - то отредактирую статьи и продемонстрирую это

А мне не понятно, почему вы оба говорите именно про алгорифмы, а не про λ-исчисление или комбинаторную логику. По моему мнению, после λ и CL работать с алгорифмами — боль и страдание. Может, поэтому все известные мне современные (кто-то ещё использует Рефал?) системы, которые пробовали в том или ином виде реализовывать определяемые пользователем алгорифмы, обмазываются императивщиной и побочными эффектами: Wolfram, TeX, sed.

Очень интересно, а можете привести какой-нибудь пример? Как я сказал в самом начале - буду рад объяснению от математика

Возьмём к примеру CL. В ней выражения (комбинаторы) применяются к другим выражениям, применение всегда одноаргументно. У каждого комбинатора есть комбанаторное свойство, которое определяет, как он будет вычисляться. Например, есть комбинатор I со свойством I[x] = x; или комбинатор K со свойством K[x][y] = x, или S[x][y][z] = x[z][y[z]].

Объекты, свойства, понятия, процессы в предметной области соответствуют своим комбинаторам. Какие-то комбинаторы просто постулируются вместе со своими свойствами, какие-то можно определить через другие (но определение должно сохранять комбинаторное свойство!), какие-то мы оставляем «неопределёнными», то есть рассматриваем их как переменные. Последние в будущем будут заменены на конкретные выражения, когда данные подъедут. Или нет.

Возьмём алгорифм Маркова. Если я работаю с матрицами, какая подстрока или какое правило соответствует понятию «матричное умножение двух неких матриц A*B»? Если я к программе добавлю ещё какие-то правила, будет ли это же (правило или подстрока) соответствовать ровно тому же понятию? Едва ли. Посмотрите, как всю программу приходится переписывать ради того, чтобы дописать другую подпрограмму.

У вас ссылка битая...

@BoomburumУ нас проблема. Я вставляю в decoded-формате, а хабр автоматом конвертирует в не-пойми-что.

http://cs.mipt.ru/wp/wp-content/uploads/2016/02/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%84%D0%BC%D1%8B-%D0%9C%D0%B0%D1%80%D0%BA%D0%BE%D0%B2%D0%B0.pdf

По теме: можете сами поискать «композиция алгорифмов маркова» — это алгоритм переписывания алгорифма так, чтобы к нему можно было дописать другой алгорифм.

Похоже, проблема на самом деле где-то ещё: ссылка не изменилась, а файл по ссылке появился. Я бы предположил кривое зеркалирование или кривой CDN, кабы не тот факт, что на домене cs.mipt.ru всего 1 IP и голый Апач.

Те примеры, которые вы описали вполне выполнятся в WL и будут работать прям в таком виде. Вам ничто не мешает писать практически так же на WL. Разве что указать, подчеркивание добавить, чтобы указать, что x - аргумент.

Вот допустим вы задаете комбинатор и говорите, что при его встрече CL возвращает другой комбинатор. Так это ведь в точности тоже самое, что и правила замены, просто там больше синтаксического сахара и может быть больше одного аргумента.

Ну так я и не утверждал, что на WL невозможно такое написать. Можно, но для этого надо выработать определённую дисциплину: на каждый символ приходится не более одного определения (Set, SetDelayed), все определение должны идти строго перед запуском вычислений и быть строго на верхнем уровне программы, нельзя использовать никакие «опасные» функции (Print, Set, Function, Module) в качестве комбинаторов и т.д. и т.п.

Проблема в том, действительно ли перед нами комбинатор или это выражение мимикрирует под комбинатор, но таковым не является. WM принципиально не позволяет ответить на подобные вопросы; из ниоткуда может в любой момент вычислений вылезти побочный эффект (самый неприятный для отладки — доопределение символов более специфичными правилами, которые не соответствуют семантике основного правила). Ставится крест на принципе Черча-Россера (результат вычисления не зависит от стратегии) и на ссылочной прозрачности (любое выражение может быть заменено на эквивалентное, полученное в результате вычисления).

P.S. моё предыдущее сообщение относилось к обсуждению алгорифмов Маркова, которые я считаю достаточно плохим формализмом по сравнению с другими. Те же λ-исчисление или комбинаторную логику можно использовать вот прямо как есть, это не «Тьюрингова трясина», хотя сложные программы на них не напишешь. Писать алгорифмы Маркова для решения хотя бы простой практической задачи нереально. «Change my mind»

В качестве рассуждения, я могу сказать, что вы всегда можете реализовать один собственный символ - Combinator и повесить на него все ограничения связанные с комбинаторной логикой в том виде, какая она есть в Lisp. Но почему нельзя создавать определения в процессе выполнения и чем опасны Print, Module, Function и Set?

кто-то ещё использует Рефал?

Как бы Вам не нравилась "имперетивщина", проблема в том, что на практике символьные вычислений все равно тесно сочетаются с численными методами. А тут уже от Рефал толку мало.

Если нужна не смесь символьных вычислений с численными методами, вот тогда используйте язык формальной математики (включая лямбда-исчисление и CL). Например, Isabelle, написанную на ML и Scala.

Подскажите, пожалуйста, а чем отличается "символьное программирование" от встречающихся терминов "символьные вычисления" и "символьная математика"? Если есть "символьное программирование" (реализующее символьные преобразования математических выражений), то можно ли тогда говорить о "численном программировании" (как реализующем численные методы решения уравнений, например)? Или это всё-таки способ вычислений, реализуемый в рамках какой-либо парадигмы программирования?

Вообще в моей голове сложилось такое мировоззрение, что символьное программирование - это одна из парадигм - такая же как функциональное программирование или ООП. По своим личным наблюдениям я могу только сказать, что оно более редкое, а поэтому менее освещенное. И я еще раз постараюсь кратко сформулировать свою мысль. Чаще всего символьное программирование подразумевает аналитические преобразования математических выражений. По крайней мере так это в популярных библиотеках, которые я встречал. Для этого же в большинстве случаев использую Haskell. Но весь язык Wolfram построен на этой концепции. В нем все является выражением, а исполнение кода происходит при помощи применения правил замен. В комментарии выше @Refridgerator говорил об алгоритмах Маркова, я в конце статьи упомянул про клеточные автоматы - и то и другое является классическим примером символьного программирования. И к тому и к другому можно свести принцип исполнения выражений на языке Wolfram. В общем главная мысль - WL не просто умеет работать с символьными выражениями, а целиком на них построен

Нет тут никакой особой парадигмы, обычное ФП, притом примитивное.

Какие ваши доказательства? Парадигмы программирования все примитивные и очень простые. Они основываются на нескольких простых принципах. ООП на инкапсуляции, наследовании, полиморфизме и абстракции. ООП код представляет собой в классическом виде объекты, которые обмениваются сообщениями. ФП смещает фокус с объектов, которые обладают "умениями" на действия или функции, которые обрабатывают данные. Символьное программирование концентрируется на преобразовании древовидных выражений при помощи правил замены. Никто вам не мешает писать ФП или ООП код на WL, но вы кажется невнимательно прочитали - я писал про те концепции, которые лежат в основе процесса вычисления кода. То что в большинстве случаев на WL пишут ФП-код - не говорит о том, по каким принципам работает ядро.

Парадигма программирования - она про то, как программист пишет код, а не про то на каких принципах работает ядро.

А правила замены в древовидных выражениях - это в чистом виде паттерн-матчинг из ФП.

паттерн-матчинг из ФП

При чем тут ФП? Или паттерн-матчинг нельзя использовать в других парадигмах?

Запрета на использование его в коде - и правда нет, но вот именно в парадигмы он "не лезет". В автоматной парадигме паттерн-матчингу попросту нет места, в ООП вместо него есть "родные" аналоги, ну а в структурном программировании добавление паттернов в операторы ветвления и цикла всё сильно усложняет (и порой смотрится костылём).

В принципе, символьное программирование как раз могло бы стать той парадигмой, которая также использует паттерн-матчинг - если бы там было что-то кроме паттерн-матчинга.

Для меня наоборот то, что на одном только паттерн матчинге можно построить реальный язык программирования является поразительным фактом. Но все таки, в WL кроме сравнения шаблонов есть и другие важные концепции:

  • код является данными

  • все данные (и код) - это древовидные структуры из символов, чисел и строк

  • вычисление кода происходит при помощи правил замен, а паттерн-матчинг только часть этого процесса

Вот принцип "код является данными" уже интереснее, он и правда в том или ином виде мигрирует между языками и тянет на мини-парадигму. Вопрос только как эту парадигму назвать, название "символьное программирование" не очень-то подходит для этого принципа.

вычисление кода происходит при помощи правил замен, а паттерн-матчинг только часть этого процесса

Как я уже писал, парадигмы - про написание кода, а не про его исполнение.

Возможно, стоит говорить не о парадигме символьного программирования, а об символьном исчислении? Или об исчислениях в принципе, ведь если разобраться, то все исчисления - так или иначе символьные.

В статье я показал сначала как пользователь может использовать символьное программирование, а затем продемонстрировал, что само ядро работает точно так же. Может чтобы избежать путаницы стоит называть это символьным исчислением

код является данными

все данные (и код) - это древовидные структуры из символов, чисел и строк

Поздравляю. Вы узнали про Лисп. То, что указано в этой статье замечательно можно прочитать в старом учебнике "Мир Лиспа" от Хювёнена и Сеппянена

Ну если это примитивное ФП, тогда напишите то же самое на JS скажем

Так JS ещё не ФП-язык. Наличия функций и замыканий для ФП недостаточно.

А вот на каком-нибудь ClojureScript, думаю, написать что-то похожее проблемой не будет.

Все таки я вас не понял. Если ФП или Символьное Программирование - это про то как программист оформляет свой код, то на JS стопроцентно можно писать как на ФП.

Где на JS паттерн-матчинг?

Я люблю холивары про стили, парадигмы и подходы программирования. Паттерн-матчинг не является синонимом ФП. Главный принцип ФП насколько я понимаю - это акцент на функциях, который обрабатывают данные. Еще важно, что данные неизменяемы - то есть они не могут мутировать как объекты, а функции всегда стабильно возвращают результат. То есть мутирующая операция на изменяемом объекте будет каждый раз менять состояние системы, а преобразование одних данных в другие функцией не должны ничего менять и обязаны что-то возвращать. Нигде я не видел паттерн-матчинг как обязательное условие ФП. Все что я описал выше работает в JS. Вы сами писали - парадигма - это то, как пишет программист. На JS если следить за руками можно легко писать функции, которые не будут ничего мутировать и однозначно возвращать результат. Его поэтому так любят там применять, потому что например в DOM возникает слишком много путаницы если каждая функция будет заниматься мутированием вместо простых и четких преобразований, в которых программист уверен

Парадигмы тоже развиваются, и ФП без паттерн-матчинга давно уже некузявое.

В смысле, писать функции-то можно, только неудобно.

Можно. Часто язык не фиксирует (ограничивает) использование парадигмы, а стимулирует (поощряет).

Громоздкость синтаксиса, отсутствие в ядре нужных библиотек и т..п. могут охладить желание писать в функциональном стиле.

T Sum<T>(IEnumerable<T> list) where T : INumber<T> => list.Aggregate((x, y) => x + y);

То же самое на Haskell:

sum = foldl1 (+) 

Ну если надо применить сложение к списку то в WL это вот так:

sum = Apply[Plus]

Для суммирования всех элементов списка есть отдельная функция Total.

@GorwinH скорее всего имел ввиду создание функции которая применяет другую функцию к списку, т.е. применение фукции высшего порядка

WL беспорно революционный язык, однако, раз он до сих пор не стал популярным, то уже и не станет - и понятно почему. Помимо революционных идей там есть и костыли, которыми Стивен подпирал то, что не получилось сделать красиво. В первую очередь это, конечно, квадратные скобки для передачи аргументов в функцию. Во вторую - всё заточено под комплексные числа и даже с обычными кватернионами там работать боль и страдания. Ну и грамматика замен вообще не интуитивно понятная, и выбор символа // для постфиксных вычислений сбивает с толку программистов на си-подобных языках. Поэтому у меня накопилась уже куча идей, как это всё можно улучшить, но реализацию их на практике осознанно оттягиваю до последнего.

Спасибо за ответ. Думаю квадратные скобки и двойной слеш это самая меньшая из проблем. На мой взгляд САМАЯ большая проблема в том, что это проприетарный язык, который 30 лет продавался только за деньги, а когда наконец до Стивена Хьюговича дошло, что опен-сорс двигает технологии вперед - было уже очень поздно.

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

На мой взгляд, главным препятствием к его популяризации стала проприетарная лицензия. Практически все популярные сейчас языки программирования - со свободными лицензиями. И я думаю, что их популярность во многом происходит именно их этого.

Upd. Извиняюсь, не обновил страницу. И в итоге написал тоже самое, что уже было написано до меня.

WL (точнее, WM) был популярен среди учёных. В широкие массы он не вышел из-за 1) конской цены, по которой он большую часть времени распространялся, когда народ присматривался к этому инструменту, ну и упомянутая другими проприетарная лицензия, и 2) рекламы и позиционирования: Стив его пиарил как шайтан-машинку, которая и тайны мироздания моделирует, или около-человеческую речь понимает, а ещё крестиком вышивать умеет, в то время как рядовому пользователю нужен обычный ЯП общего назначения с низким порогом входа и с хорошей библиотекой для решения его, пользователя, задач (собственно, поэтому Fortran и Python заимели популярность в своё время). Причём WL является таким ЯП, но чтобы Стив так сказал, нужно гордостью подавиться. Посмотри на переводные статьи про WL на хабре — много ли желания возникает использовать его для решения частных бытовых задач?

Костыли, конечно, есть, но это не квадратные скобки. К слову, WL один из немногих языков, в которых группировочные скобки для разбора выражений с инфиксными операторами и скобки, входящие в состав синтаксического выражения (список аргументов, кортеж и т.п.), различаются графически. К примеру, в JS скобки в x = (a, b) и x = f(a, b) выполняют разную роль.

Также // не проблема, ведь всегда можно использовать префиксную запись.

К костылям я бы отнёс императивность интерпретатора, которая вынуждает внимательно следить за тем, в каком состоянии находится ядро (kernel), в т.ч. какие правила записаны в *Values

Вот про последний костыль не понял, можете подробнее описать?

1) ну не такая уж и конская цена. Не так уж и давно, до известных событий, лицензия для индивидуального исследователя обошлась мне в 25 т.р. Это в 2 раза меньше суммы, потраченной мной на велосипед далеко не премиум класса, состоящего в основном из алюминиевых труб по технологиям 100-летней давности.

2) с неадекватной рекламой согласен. Для ЯП общего назначения он не подходит просто по определению. Причём, при сильном желании, это тоже можно было реализовать - но для этого Стивен должен быть ещё и программистом, чтобы точно знать, что этим самым программистам нужно для полного счастья.

 ну не такая уж и конская цена.

Я мог ошибиться, описал субъективное видение из далёкого прошлого. Когда я покинул НИИ и у меня кончилась действующая лицензия, решил купить у них. Единственная подходящая лицензия стоила за год как 1/4 моей месячной зарплаты. Естественно я не стал её приобретать.

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

Для ЯП общего назначения он не подходит просто по определению.

В текущем виде — да. Впрочем, я на нём и XML-файлы парсил и анализировал, и с файловой системой работал, и строил иллюстрации к статьям, и числодробилку писал. У языка есть потенциал стать вполне хорошим скриптовым языком.

На самом деле цена действительно очень высокая. Ведь если вы хотите пользоваться всегда новой версией, то придется покупать заново лицензию раз в 1-2 года. Велосипед послужит намного дольше

  1. Когда то 3-4 года назад приобрели 2 сетевые лицензии за 2500$. По сравнению с специализированным геофизическим программным обеспечением эта цена не была высокой.

  2. На работе у нас есть сложности с установкой на рабочие станции бесплатного и open-source ПО. В тоже время к установке Wolfram Language вопросов не возникает.

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

Что действительно раздражает - так это политика Вольфрама по непризнанию собственных ошибок. Это когда считаешь интеграл, а в результате пропадает переменная интегрирования или вообще ноль. Или внезапное падение оболочки, или внезапная утечка памяти. Нет у них на сайте списка известных проблем.

как писал @JerryI, на данный момент не смотря на все проблемы и сложности существуют пакеты для WL, которые позволяют писать веб-приложения, ботов для различных приложений, анализировать данные, тренировать нейросети и др... то есть я хочу сказать что силами энтузиастов постепенно в языке появляется то, что интересно программистам и то, что делает WL языком общего назначения. Вот вам чего не хватает чтобы пользоваться WL как языком общего назначения?

Если строго по имеющейся расплывчатой терминологии, то, с одной стороны, любой тьюринг-полный язык можно считать языком общего назначения, а с другой стороны - тот же Rust можно считать предметно-ориентированным языком, так как он разработан для программирования, ориентированного на безопасность.

Можно ли на WL написать модуль ядра Linux? А насколько удобно на C или Rust заниматься символьными вычислениями?

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

Я все таки пытаюсь выяснить про что-то, что есть в популярных языках чего вам не хватает. Я лично раньше все время хотел, чтобы была возможность писать веб-приложения на WL. Теперь я сам это сделал и оно работает. А вам чего не хватает? На Python тоже не пишут ядро операционной системы, но он считается языком общего назначения и есть области где он хорош, а где не очень.

Я лишь пытаюсь донести до Вас свое личное мнение о том, что швейцарский нож, конечно круто, но по отдельности нож, отвертка, ножницы, плоскогубцы и шило намного удобней в работе.

Чего не хватает? ООП, перегрузки операторов, строгой типизации, событийно-ориентированного программирования, дата-ориентированного программирования, параллельного программирования с примитивами синхронизации, средств отладки, скорости исполнения.

Но ведь все это есть, просто оно выглядит необычно и нужно привыкнуть. Но это в любом языке так. С ООП только спорно, но матируемый объекты с наследованием я сделал сам

Пожалуй про это стоит написать отдельно небольшое руководство.

все уже придумано было из того, что описываете, причем довольно давно. Лет 10 назад всплывали такие вопросы в Google Groups и на них были ответы. Просто люди забыли, или лень было разбираться

и ООП, и шайтан-ФП. В этом и соль, что можно описать диалект языка под задачу и потом уже решать на нему эту самую задачу. Компиляцию в LLVM завезли недавно там хорошая типизация кстати и еще CUDA из коробки, до этого было с помощью CCompiler. Debugger там есть, пусть и так себе на мой взгляд. Если хочется что-то экзотического типа event-loop, он там почти есть из коробки (скажем, сокеты или другие внешние события дергают обработчики асинхронно, можно любую логику и промисы при желании реализовать). Если этого мало, то есть LibraryLink, которая подключит любую произвольную dll и можно наворотить делов в ядре...ух

В общем вероятно ни о чем спорите вдвоём. Минусы у всего есть, как писали выше, универсального бриллиантового тула не выйдет, но ЯП шириной области применения как минимум не уже, чем Python точно выходит. Только своей проприетарностью отличается резко в не самую хорошую сторону.

все уже придумано было из того, что описываете

Ну давайте будем честными. Далеко не всё из перечисленного, и только то, что хоть как-то ложится на исходную концепцию языка. Веб-сервер можно сделать только потому, что для этого целенаправленно сделали SocketListener и прочую инфраструктуру. А DSP-процессор нельзя сделать, потому что средств для потоковой обработки аудио ещё не завезли.

есть LibraryLink, которая подключит любую произвольную dll и можно наворотить делов

dll же на каком-то другом языке пишется? Соответственно сразу и возникает вопрос, какую часть логики писать на этом другом языке, а что оставить Вольфраму. И лично для меня всё выше перечисленное намного, намного комфортнее делать на си++ или c#. А на Вольфраме - всё то, ради чего он и создавался - вычисления, прототипирования, генерация таблиц или кода, ну или просто как справочник, что само по себе уже немало. Интереснее наоборот, Вольфрам вызывать из c#.

В общем вероятно ни о чем спорите вдвоём

Споры в комментариях увеличивают количество просмотров и двигают статью в топы) Ещё можно заглянуть на WOLFRAM Demonstrations Project и заметить, что демки по оформлению там довольно однообразные.

А прием здесь демонстрации? Они все были сделаны примерно в одно время, когда WRI придумали CDF и решили сделать сайт с кучей примеров. А лучший пример - это визуализация

Мы не использовали SocketListener, но его тоже можно использовать в текущей реализации. Но спору нет - без него бы веб-сервер не получился

@Refridgerator,я люблю такие вопросы)

SocketListener

@KirillBelovTestнедавно его на помойку выкинул и написал свой

Ещё можно заглянуть на WOLFRAM Demonstrations Project и заметить, что демки по оформлению там довольно однообразные.

Вот кстати есть WLX и там можно разгуляться с оформлением на CSS/HTML

КДПВ
КДПВ

Ну и еще вот на том же WL и JS + кастомные сокеты написанный с нуля фроентенд)

А DSP-процессор нельзя сделать, потому что средств для потоковой обработки аудио ещё не завезли.

Да тут трудновато, согласен. Здесь явно нужен модуль на Си/++/ust.

Споры в комментариях увеличивают количество просмотров и двигают статью в топы)

Сложно не согласиться)

dll же на каком-то другом языке пишется? Соответственно сразу и возникает вопрос, какую часть логики писать на этом другом языке, а что оставить Вольфраму.

Да, я лично не спорю. Мне всегда нравилось делать композиты в роде WL + C + OpenCL + HTML/CSS для морды. Я имел ввиду конкретно, что писать ООП и прочие штуки можно, если хочется. Даже если совмещать два языка при разработке, удобнее, когда и там и там есть, скажем, объекты и они передаются бесшовно друг другу (что как раз возможно).

Ого! Вот это да! Неужели на гифке происходит real time calculation, т.е. в каждый момент времени после обновления кадра браузер получает новые данные для отрисовки и все изменения из UI пересылаются так же? Но ведь мне выше говорили, что WL медленный!!! ;-)

Ну все упирается в скорость сериализации данных чтобы отправить их по TCP. А так да

Чуток дополню.

Квадратные скобки понятно почему, это чтобы friendly для математических выражений. Раньше было принято что программист также должен являться математиков. А штуки типа // и всякие @ это же сокращения, которые в принципе не обязательны. В целом ну больше косметические вещи описываете, к которым, разумеется может быть очень чувствительно общество

Кстати насчет практических задач. Там в репе @KirillBelovTestесть и веб сервера и телеги ботов зоопарк. Те если забыть про неотделимую стандартную библиотеку и юзать бесплатный Wolfram Engine то будут все бытовые задачи

UPD блин уже ответили ;)

Спасибо большое, конечно же я уверен в том, что Хабр все еще торт и уважаемые читатели знают Константина не только как героя популярного мема =)

Странно, что в статье не прозвучало ни слова сравнения с динозавром символьного программирования - Lisp. Все же наиболее мощная и известная OpenSource система символьного программирования реализована именно на нем (Maxima)

Как я в самом начале и написал - я не знаю Lisp и поэтому не могу сравнить. Я поверхностно знаком с ним и знаю то, что WL и Lisp очень похожи концептуально. Но я с вами согласен - сравнение Lisp и WL было бы очень полезным делом

Не в качестве критики, а в качестве идеи для следующей статьи.

Можно сравнить имеющиеся в WL библиотеки с библиотеками Maxima. Не вдаваясь в детали реализации и в отрыве от языка. Подключить себе в проект Maxima можно даже с весьма поверхностными знаниями Lisp. Хотелось бы представлять, даст ли заметный эффект переход на WL или нет. Например, насколько успешно уже имеющиеся библиотеки решают хотя бы не дифференциальные системы уравнений в обоих системах.

Отличная идея, спасибо! Как только смогу я попытаюсь сравнить WL и Maxima!

Автор статьи в курсе кто на фотографии в заголовке?

Всегда с опаскою читаю публикации, содержащие слово "парадигма" (на текущий встречается дважды в собственно статье и 18 раз в комментариях), и не очень разбираюсь в различных подходах к программированию (пользовался лишь некоторыми, которые, по-видимому, считаются устаревшими), хотя и написал когда-то систему, в составе которой, среди прочего, имеются процедуры манипулирования символьными выражениями. Понимаю только, что реальные программные системы с трудом можно втиснуть в рамки какого-то одного подхода, и поэтому невозможно делать вообще все, придерживаясь одной универсальной методологии, как не стоит писАть бухгалтерскую учетную систему на Лиспе, решение диффуравнений на Коболе или АСУТП на SQL. Символьные оперции действительно очень часто связаны с численными методами, но заменить собою численные методы они не могут. По-видимому, реальной перспективой является создание средств для взаимного проникновения различных подходов (в виде API), а не совершенствование каждого подхода, чтобы он вытеснил все остальные, как бы заманчиво это не выглядело

АСУТП на SQL можно написать, промежуточный уровень между контроллерами и верхним уровнем, имхо.

На самом деле я использовал слово "парадигма" чтобы избежать тавтологии, но на Хабре любят обсуждать парадигмы и подходы, так как это такая тема, которая не имеет четкого определения. Если бы я писал более точно, то это было бы во-первых скучно, во-вторых здесь в комментариях не собралось бы столько замечательных людей. Вы правы на счет того, что в один подход невозможно втиснуть абсолютно все. Но этого и нет. Я пытался рассказать только о том, почему WL называют символьным языком программирования. Но это не исключает всего остального. Язык так же называют процедурным и функциональным. А еще на нем можно сделать ООП.

Про последнюю часть вашего комментария. Примерно так я и поступаю на самом деле со своими пет-проектами. Мне нравится делать то, чего в языке еще нет, но очень хочется. Часто я решаю это в виде API стороннему сервису, или в виде использования скомпилированной библиотеки на другом языке. В первую очередь я всегда решаю практическую задачу, но мои предпочтения на стороне WL благодаря его дизайну. Например, я сделал клиент для WebSocket протокола. Им можно пользоваться как в других языках, но с особенностями WL. Вот только конкретно в этой библиотеке я не делал сам протокол, а взял готовую реализацию на Java.

Тоже мне, контурные интегралы они выкатили только в версии 13.3, этой зимой, на 35, ядрить их, году существования пакета. В статусе Experimental.

А уж с какой радостью оно ядра роняет и память жрёт, - ни в сказке сказать, ни пером описать, нормальный программист плюнет-разотрет и напишет сам, а ученые да, и не такое терпят.

Их терпят только за то, что остальная сивольная алгебра еще беднее по функционалу, а тут по крайней мере порог входа низкий и можно студентам-непрограммистам задачи ставить, а те могут со стак-оверфлова невозбранно копипастить.

контурные интегралы

остальная сивольная алгебра еще беднее по функционалу

А что не так в Maxima с контурными и поверхностными интегралами?

А можете пожалуйста рассказать какого рода проблемы у вас чаще всего приводили к падению ядра и привести пример кода или хотя бы показать какую задачу вы решали? Ну и про потребление памяти тоже хотелось бы подробнее

Понимаю о чем вы частично. Всегда есть альтернативный путь, падает в основном из-за оболочки. Все численные функции очень стабильны, нестабильна динамика (что опять не часть языка а функция фронта) и всякие «опасные» символьные вычисления.

Как скриптовый язык (wolfram engine) он вот уже три года работает на сервере у меня университете и хранит и обрабатывает и показывает через веб морду 1000 (не фигура речи) экспериментальных спектров со всяких машин от одной группы экспериментаторов.

И да, оно падает периодически из за утечки памяти, но на практике местный свитч/роутер чудит примерно с такой же регулярностью

Просто Стив склоняет привязываться к нативной платной блокнотной среде, хотя если забить на это и просто юзать сам язык без стандартных инструментов все идет круто. И фронт при желании можно свой прикрутить (уже сделано, аж три вариации для vscode для jupiter и еще самописный на js). И ничего там не падает.

Согласно вашему описанию (как я его понял) ядро последовательно сопоставляет шаблоны до тех пор, пока новых подстановок далее не находится. Следовательно, должны отсутствовать взаимно-обратные правила (т.е. A -> B вместе с B -> A) или группы правил (например, A -> B, B -> C и C -> A), иначе может случиться зацикливание. Отсюда я заключаю, что реальный процесс вычисления должен быть сложнее, с возвратами и предотвращением циклов.

Если есть бесконечная рекурсия - ядро в какой-то момент выдает сообщение о том, что лимит рекурсии исчерпан. Если есть бесконечный цикл, который не удается определить как рекурсию - то вычисления так и будут висеть бесконечно. Это является ответственностью пользователя. Конечно хочется чтобы такое предотвращалось, но вот такой принцип языка

А можно символьные вычисления для оптимизации регулярных выражений (классических) приспособить?

Есть у меня, положим, тыща альтернатив, я хочу чтобы общие префиксы/постфиксы сами за скобки вынеслись.

Так быстро мне трудно сказать, но в WL есть очень крутая штука, которая называется StringExtression.
Например такой код проверят что строка соответствует шаблону по первому слову:

StringMatchQ["string1 string2", "string1" ~~ __]

А вот такая штука вырежет текст из разметки XML:

StringCases["<div>text</div>", "<div>" ~~ text__ ~~ "<div>" :> text]

Вот такой проверит на соответствие простому номеру телефона:

StringMatchQ["+79991234568", {"89", "+79"} ~~ NumberString]

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

Может быть позже я напишу про них отдельную статью

На множестве регулярных выражений определены: конкатенация и альтернатива. У конкатенеции приоритет. Скобки меняют порядок действий. С ними хотелось бы работать аналогично тому, как мы работаем с выражениями, содержащими умножение и сложение.

А StringExtression можно символьно преобразовывать? Получать из одних StringExtression другие? Выносить за скобки общий префикс или наоборот - раскрывать скобки?

Ну вообще все это можно делать, только я пока не могу придумать задачу на которой это можно показать. StringExpression точно такое же выражение как и все остальное. В WL есть регулярные выражения, но они классические и представляются в виде строки, поэтому как с символами с ними не поработаешь. У вас есть какой-нибудь пример чтобы я мог показать как можно изменять сами строковые выражения?

Ну, вот, например:

день ::= \d{1,2}
месяц ::= \d{1,2}
год ::= \d+
годiso ::= \d{4}
месяцiso ::= \d{2}
деньiso ::= \d{2}

Дальше я использую ранее определённые элементы как переменные, и записываю множество форматов:

значение ::=
::= день\.месяц\.год
::= месяц\.год
::= год
::= годiso-месяцiso
::= годiso-месяцiso-деньiso

Дальше я хочу это множество форматов объединить через "|" и оптимизировать полученное выражение выносом за скобки общих префиксов, чтобы я мог визуально посмотреть, что за язык получается.

Собственно, Regex - не принципиально. Если ту же задачу можно сделать с помощью StringExtression - тоже покатит.

В принципе я вас понял. Это вполне можно реализовать, только прежде всего я должен уточнить, что для StringExpression не существует встроенных правил упрощения. Какие вы сами придумаете правила - такие и будут работать. Еще это на мой взгляд довольно нетривиальная задача, но вполне решаемая. Смотрите, в строковых выражениях альтернатива - это список выражений, т.е. записывается вот так:

StringMatchQ["123", {"123", "456"}] === True
StringMatchQ["456", {"123", "456"}] === True

А конкатенация записывается собственно вот так:

StringMatchQ["1.2", DigitCharacter ~~ "." ~~ DigitCharacter] === True
StringMatchQ["2.1", DigitCharacter ~~ "." ~~ DigitCharacter] === True

Тогда можно создать функцию, которая "выносит" за скобочки общий "множитель". Назовем ее sexFactor, по аналогии со встроенной функций Factor, но с указанием того, что она специфична для StringEXpression:

ClearAll[lstseq]
ClearAll[sexFactor]

lstseq[l_List] := Sequence @@ l
lstseq[e_] := e
lstseq[e__] := StringExpression[e]

sexFactor[{e1___, Longest[x__] ~~ e2__, e3___, Longest[x__] ~~ e4__, e5___}] := 
 sexFactor[{x ~~ {lstseq@e2, lstseq@e4}, e1, e3, e5}]

sexFactor[{e1___, e2__ ~~ Longest[x__], e3___, e4__ ~~ Longest[x__], e5___}] := 
 sexFactor[{{lstseq@e2, lstseq@e4} ~~ x, e1, e3, e5}]

sexFactor[s : {__}] := s

Что делает эта функция? Находит пары совпадающих частей слева или справа от конкатенации и выносит их за скобки альтернативы. Это не идеальная реализация, плюс количество подчеркиваний может запутать, но эта штука работает для вашего примера. Теперь нам нужно задать шаблоны для дат вот так:

date = {
  year ~~ "." ~~ month ~~ "." ~~ day, 
  year ~~ "." ~~ month, 
  year
}

И попробуем применить вынесение за скобки:

sexFactor[date]

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

day = {{"1", "2"} ~~ DigitCharacter, Except["0", DigitCharacter], "30", "31"};
month = {{Except["0", DigitCharacter], ""} ~~ Except["0", DigitCharacter], "10"};
year = {"", "19", "20"} ~~ Repeated[DigitCharacter, 2];

Они конечно переусложнены, зато имеют больше ограничений ну и показывают дополнительные примеры использования StringExpression. Собственно на этом все. Теперь мы можем проверять строки на соответствие датам:

Кстати подобным образом можно сделать не только вынесение за скобки множителя, но раскрытие скобок - нужно только придумать дополнительные правила.

Конечно я не говорю, что это легко и я отдаю себе отчет в том, что даже при учете того, что выше всего около десятка строк кода, но додуматься до такого можно только если уже достаточно долго пользоваться WL и привыкнуть к шаблонам. В общем надеюсь, что ответил на вопрос по манипуляцию со строковым выражением как с символьным

RegEx тут все же смотрится более органично.

Думаю это дело привычки. Довольно часто строковые выражения длиннее, т.к. в WL принято встроенные функции называть полными словами на английском, но для меня на данный момент намного читабельнее, плюс еще раз хочу отметить, что манипулировать ими можно как обычными выражениями без парсинга строк. Ну и я специально долго не думал, а побольше накидал. Можете быстро написать RegEx, который проверяет месяц от 1 до 12? Самый простой вариант на WL будет

month = ToString /@ Range[12]

Привычка - это одно, а вот пытаться соревноваться с RegEx в краткости и производительности - совсем другое. Ведь любой из перечисленных форматов парсится одной строкой (в квадратных скобках необязательные элементы):

D[D].M[M][.YY[YY]]
M[M]/D[D][/YY[YY]]
YY[YY]-M[M]-D[D]
YY[YY]MMDD
включая со строковым указанием месяца, например, на русском или английском:
Mon[th] D[D],YY[YY]
Mon/D[D]/YY[YY]
D[D]-Mon-YY[YY]
D[D] Month YY[YY]
и т.п.

Не спорю с полезностью регулярных выражений, но вы слишком конкретно подходите к задаче из примера. Я пытался показать возможность манипулировать самим шаблоном, а не демонстрировал краткость и производительность. И у того и у другого подхода есть свои плюсы и минусы. Давайте с другой стороны зайдем. Допустим в регулярных выражениях есть заранее подготовленные шаблоны для дат. Но вот теперь мне пришла в голову цель выбрать из списка номеров телефонов только те, которые заканчиваются на любое простое число из последовательности Фибоначчи. Какая быстрая и короткая запись для этого шаблона?

Номер телефона имеет длину всего 11 цифр. Поэтому нам нужно не более 55 первых чисел Фибоначчи. Треть их по определению четная. Мне лениво сейчас проверять на простоту остальные 36 чисел. Но тест BPSW тут точно применим. В результате будем иметь последовательность всего из 10-15 чисел.

Можно, конечно, их этого десятка чисел сформировать строку регулярного выражения, но это уж слишком примитивное использование RegEx.

Это не то. Автоматически формат даты он определять не умеет.

Мне интересно именно автоматическое преобразование выражений.

Никого же не удивляет, что я использую пакет компьютерной алгебры для преобразования выражений к удобному мне виду? Скажем, из пяти уравнений вывести общую формулу (километровую), факторизовать её (чтобы не так страшно выглядела) и потом использовать в какой-то программе, где она была нужна.

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

Кстати, а Вольфрам автоматически StringExpression<=> Regex преобразовывать не умеет?

Автоматическое в каком смысле? Как я сказал выше - для StringExpression или для RegExp нужно сначала создать правила преобразования и тогда можно будет все делать автоматически. По умолчанию аналогов Factor или Expand для StringExpression нет

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

Прочитав "символьное программирование", почему-то подумалось что будет что-то про математическую оптимизацию, которая так же называется "математическое программирование". Уж больно там формулировки похожие: линейное, нелинейное, квадратичное, дискретное, целочисленное и т.д., и всё программирования. Тем более, что символьные вычисления там очень даже используются.

Касательно самой статьи: спасибо, интересно, но ничего кардинально нового не увидел (впрочем, чего ещё ожидать от языка 30+ летней давности). Сейчас есть языки и поинтересней. В той же Julia можно и традиционными символьными вычислениями заниматься (на базе символов), так и то же самое проделывать с кодом (привет лисп). Плюс метчинг на базе системы типов через multiple dispatch. Можно сделать всё то же самое, что здесь, и даже больше, да ещё и быстрее.

спасибо, интересно, но ничего кардинально нового не увидел (впрочем, чего ещё ожидать от языка 30+ летней давности). Сейчас есть языки и поинтересней. 

Спору нет. Есть новые и старые sexy языки. Это чисто вкусовщина

Можно сделать всё то же самое, что здесь, и даже больше, да ещё и быстрее.

Вероятно вы очень плохо знакомы с WL, что в принципе неудивительно, так как статья только верхушку айсберга затрагивает. Благо @KirillBelovTestеще напишет для интересующихся. «Тоже самое и даже больше» - тут как черное назвать белым)

Можно сделать всё то же самое, что здесь, и даже больше, да ещё и быстрее.

А что больше-то? Можете пожалуйста сказать, чего такого нет в WL, но есть в Julia?
Ну то есть простой пример на Julia, а я постараюсь ответить - есть ли подобное в WL.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории