Математика: что символическое программирование?

Я большой поклонник Стивена Вольфрама, но он определенно не стесняется делать свой собственный рог. Во многих ссылках он превозносит Mathematica как другую символическую парадигму программирования. Я не являюсь пользователем Mathematica.

Мои вопросы: что это символическое программирование? И как он сравнивается с функциональными языками (такими как Haskell)?

Ответ 1

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

Например, вы можете указать следующее правило

area := Pi*radius^2;

В следующий раз, когда вы используете area, он будет заменен на Pi*radius^2. Теперь предположим, что вы определяете новое правило

radius:=5

Теперь, когда вы используете radius, он будет переписан в 5. Если вы оцениваете area, он будет перезаписан в Pi*radius^2, который вызывает правило перезаписи для radius, и вы получите Pi*5^2 в качестве промежуточного результата. Эта новая форма вызовет встроенное правило перезаписи для операции ^, чтобы выражение получило дальнейшее переписывание в Pi*25. На этом этапе переписывание прекращается, потому что нет применимых правил.

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

add[a_,b_]:=a+b

Теперь add[x,y] переписывается в x+y. Если вы хотите добавить только для чисел a, b, вместо этого вы можете сделать

add[a_?NumericQ, b_?NumericQ] := a + b

Теперь add[2,3] переписывается в 2+3 с использованием вашего правила, а затем в 5 с использованием встроенного правила для +, тогда как add[test1,test2] остается неизменным.

Здесь приведен пример правила интерактивной замены

a := ChoiceDialog["Pick one", {1, 2, 3, 4}]
a+1

Здесь a заменяется на ChoiceDialog, который затем заменяется номером, который пользователь выбрал в появившемся диалоговом окне, что делает правило количества чисел и триггеров заменой для +. Здесь ChoiceDialog как встроенное правило замены в строках "заменить ChoiceDialog [некоторые вещи] значением кнопки, которую пользователь нажал".

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

 solve[x + 5 == b_] := (result = b - 5; result /; result > 0)

Здесь solve[x+5==20] заменяется на 15, но solve[x + 5 == -20] не изменяется, потому что не применяется правило. Условие, препятствующее применению этого правила, - /;result>0. Оценщик в основном рассматривает потенциальный вывод приложения правила, чтобы решить, продолжать ли это.

Аналитик Mathematica жадно переписывает каждый шаблон с одним из правил, применимых к этому символу. Иногда вы хотите иметь более тонкий контроль, и в этом случае вы можете определить свои собственные правила и применить их вручную, как это.

myrules={area->Pi radius^2,radius->5}
area//.myrules

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

Один недостаток текущей версии Mathematica возникает, когда вам нужно использовать оценщик по умолчанию Mathematica (использовать Integrate, Solve и т.д.) и хотите изменить последовательность оценки по умолчанию. Это возможно, но сложный, и мне нравится думать, что некоторая будущая реализация символического программирования будет иметь более элегантный способ контроля последовательности оценки

Ответ 2

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

Mathematica сильно использует эту возможность. Еще больше, чем LISP и Prolog (IMHO).

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

r,1,2
g,3,4

Я прочитал этот файл в:

Import["somefile.csv"]
--> {{r,1,2},{g,3,4}}

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

Итак, теперь я применяю преобразование к результату:

% /. {c_, x_, y_} :> {c, Disk[{x, y}]}
--> {{r,Disk[{1,2}]},{g,Disk[{3,4}]}}

Не останавливаясь на деталях, все, что произошло, состоит в том, что Disk[{...}] было обернуто вокруг двух последних чисел из каждой строки ввода. В результате все еще данные/код, но все же инертны. Другое преобразование:

% /. {"r" -> Red, "g" -> Green}
--> {{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}

Да, все еще инертен. Однако, по замечательному совпадению, этот последний результат просто является списком действительных директив в Mathematica для встроенного языка для графики. Последнее преобразование, и все начинает происходить:

% /. x_ :> Graphics[x]
--> Graphics[{{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}]

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

alt text

Но веселье не останавливается на достигнутом. Под всем этим синтаксическим сахаром мы все еще имеем символическое выражение. Я могу применить другое правило преобразования:

% /. Red -> Black

alt text

Presto! Красный круг стал черным.

Это такой "символьный толчок", который характеризует символическое программирование. Подавляющее большинство программ Mathematica имеет такой характер.

Функциональный и символический

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

Можно было бы рассматривать символическое программирование как ответ на вопрос: "Что произойдет, если я попытаюсь моделировать все, используя только преобразования выражений?" Функциональное программирование, напротив, можно рассматривать как ответ на вопрос: "Что произойдет, если я попытаюсь смоделировать все, используя только функции?" Подобно символическому программированию, функциональное программирование позволяет быстро создавать слои абстракций. Пример, который я привел здесь, можно легко воспроизвести, скажем, в Haskell, используя подход функциональной реактивной анимации. Функциональное программирование - это функциональный состав, функции более высокого уровня, комбинаторы - все отличные вещи, которые вы можете выполнять с функциями.

Mathematica явно оптимизирована для символического программирования. Можно писать код в функциональном стиле, но функциональные функции в Mathematica на самом деле являются лишь тонким шпоном над преобразованиями (и пропуская абстракцию, см. Сноску ниже).

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

Заключительные замечания

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


Утечка функциональной абстракции в математике?

Yup, негерметичный. Попробуйте это, например:

f[x_] := g[Function[a, x]];
g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]];
f[999]

Сообщается и подтверждается WRI. Ответ: избегайте использования Function[var, body] (Function[body] в порядке).

Ответ 3

Как уже упоминалось ранее, Mathematica много переписывает термин. Возможно, Haskell - это не лучшее сравнение, но Pure - хороший функциональный язык перезаписи (который должен быть знаком людям с фоном Haskell). Возможно, прочитав свою страницу Wiki при перезаписи терминов, вы проясните несколько вещей для вас:

http://code.google.com/p/pure-lang/wiki/Rewriting

Ответ 4

Mathematica использует термин переписывание сильно. Язык предоставляет специальный синтаксис для различных форм перезаписи, специальной поддержки правил и стратегий. Парадигма не в том, что "новая" и, конечно же, она не уникальна, но они определенно находятся на кровоточащей грани этого "символического программирования", наряду с другими сильными игроками, такими как Axiom.

Что касается сравнения с Haskell, ну, вы можете сделать переписывание там, немного помогая от лома вашей библиотеки шаблонов, но это не так просто, как в динамически типизированной Mathematica.