По сути, вы спрашиваете о разнице между вычислительной мощью и тем, что обычно называют выразительной силой (или просто выразительностью ) языка (или системы вычислений).
Вычислительная мощность
Вычислительная мощность относится к тому , какие проблемы языка можно вычислить. Наиболее известным классом вычислительной мощности является тот, который эквивалентен универсальной машине Тьюринга . Существует множество других систем вычислений, таких как машины произвольного доступа , λ-исчисление , комбинаторное исчисление SK , µ-рекурсивные функции , WHILE
программы и многие другие. И, как выясняется, все они могут имитировать друг друга, что означает, что все они имеют одинаковую вычислительную мощность.
Это порождает тезис Черч-Тьюринга (названный в честь Алонзо Черча, который создал λ-исчисление, и Алана Тьюринга, который создал Универсальную Машину Тьюринга). Тезис Черча-Тьюринга является гипотезой вычислимости с двумя аспектами:
- все вычислительные системы, способные к общим вычислениям, одинаково мощны, и
- человек, следуя алгоритму, может точно вычислить функции, которые может вычислить машина Тьюринга (и, следовательно, любая из других систем).
Второе, однако, более важно в области философии сознания, чем информатика.
Однако есть две вещи, о которых не говорит Тезис Черча-Тьюринга , которые очень актуальны для вашего вопроса:
- насколько эффективны различные симуляции и
- насколько удобно кодирование проблемы.
Простой пример для (1): на компьютере с произвольным доступом копирование массива занимает время, пропорциональное длине массива. Однако на машине Тьюринга это занимает время, пропорциональное квадрату длины массива, поскольку машина Тьюринга не имеет произвольного доступа к памяти, она может перемещаться по ленте только по одной ячейке за раз. Следовательно, необходимо скопировать n элементов массива n раз, чтобы скопировать их. Таким образом, разные модели вычислений могут иметь разные характеристики производительности даже в асимптотическом случае, когда мы пытаемся абстрагироваться от деталей реализации.
Примеров (2) предостаточно: и λ-исчисление, и Python полны по Тьюрингу. Но вы бы предпочли написать программу на Python или в λ-исчислении?
Есть еще одна третья морщина, которую я обходил до сих пор: все эти оригинальные системы были разработаны логиками, философами или математиками, а не учеными-компьютерщиками ... просто потому, что компьютеров и, следовательно, компьютерных наук не было. Все это восходит к началу 1930-х годов, даже до самых первых экспериментов Конрада Цузе (которые в любом случае не были программируемыми и / или не завершенными по Тьюрингу). Они говорят только о «вычислимых функциях на натуральных числах».
Теперь, как выясняется, можно многое выразить в виде функций на натуральных числах - в конце концов, наши современные компьютеры даже обходятся намного меньше (в основном 3-4 функции для чисел 0 и 1, и все) ), но, например, какую функцию выполняет операционная система?
Это понятие I / O, побочных эффектов, взаимодействующих с окружающей средой, не охвачено идеей «функций над натуральными числами». И тем не менее, это своего рода важно, поскольку, как Саймон Пейтон Джонс однажды выразился «Все чисто функции без каких - либо побочных эффектов делает, сделайте ваш процессор горячим» , к которому член аудитории ответили « на самом деле, что является побочным -эффект тоже!
Эдвин Брэди , дизайнер Idris , (только половина) в шутку использует (я не знаю, изобрел ли он его) термин «Tetris-complete», чтобы выразить эту разницу между «может вычислять любую вычислимую функцию на натуральных числах» и «может использоваться для написания нетривиальных программ, взаимодействующих со средой ". Еще более иронично, он демонстрирует это, внедрив клон Космических захватчиков в Идрисе , но он говорит, что уверен, что Тетрис превращается в Космических захватчиков.
Еще одна вещь, на которую следует обратить внимание, заключается в том, что не только эквивалентности по Тьюрингу не обязательно достаточно для того, чтобы говорить о написании «полезных» программ, но и, возможно, OTOH даже не является необходимым . Например, SQL стал эквивалентным по Тьюрингу только с ANSI SQL: 1999 , но до этого он все еще был полезен. На самом деле, некоторые могут возразить, что создание его эквивалентного по Тьюрингу вовсе не увеличило его полезность. Существует много доменных языков, которые не эквивалентны по Тьюрингу. Язык описания данных обычно нет (и не должен быть). Очевидно, что Total Languages не может быть эквивалентным по Тьюрингу, но вы все равно можете записывать в них циклы событий, веб-серверы или операционные системы. Есть также языки, которые эквивалентны по Тьюрингу, но где это фактически считается ошибкой.
В общем, эквивалентность по Тьюрингу не очень интересна, если только вы не хотите статически анализировать программы.
Выразительность
Если предположить, что наша вычислительная система достаточно мощна в вычислительном отношении, чтобы вообще решить нашу проблему, то, что нам нужно сделать дальше, - это выразить наш алгоритм для решения этой проблемы в некоторой формальной записи для этой системы. Другими словами: нам нужно написать программу на каком-нибудь компьютерном языке. Вот где приходит понятие выразительности .
По сути, это означает, насколько «легко» или «приятно» писать нашу программу на нашем конкретном языке программирования. Как видите, это понятие довольно размытое, субъективное и скорее психологическое, чем техническое.
Однако есть попытки более точных определений. Самым известным (и самым строгим из известных мне) является Матиас Феллайзен в своей статье « О выразительной силе языков программирования» (первые две страницы содержат мягкое введение, остальная часть статьи более мясистая).
Основная интуиция заключается в следующем: при переводе программы с языка на другой язык некоторые из изменений, которые вам нужно сделать, содержатся локально (например, превращение FOR
циклов в WHILE
циклы или циклов в условные GOTO
s), а некоторые требуют изменения глобального Структура программы.
Когда вы можете заменить одну функцию одного языка другой функцией другого языка только локальными преобразованиями, то считается, что эти функции не влияют на выразительность. Это называется синтаксическим сахаром .
С другой стороны, если это требует изменения глобальной структуры программы, то язык, на который вы переводите, считается неспособным выразить эту функцию. И язык, с которого вы переводите, называется более выразительным (по отношению к этой функции).
Обратите внимание, что это дает объективно измеримое определение выразительности. Также обратите внимание, что это понятие зависит от контекста и является сравнительным. Таким образом, если каждая программа на языке A может быть переведена на язык B только с локальными изменениями, и есть хотя бы одна программа на языке B, которая не может быть переведена на A только локальными изменениями, то язык B строго более выразителен, чем язык, Однако более вероятный сценарий состоит в том, что многие программы на обоих языках можно переводить туда и обратно, но есть некоторые программы на обоих языках, которые нельзя перевести на другой. Это означает, что ни один язык не является строго более выразительным, чем другой, они просто имеют разные функции, которые позволяют различным программам выражаться по-разному.
Это дает формальное определение того, что значит быть «более выразительным», но все же не отражает психологические понятия, лежащие в основе этого явления. Например, синтаксический сахар, согласно этой модели, не увеличивает выразительную силу языка, потому что он может быть переведен с использованием только локальных изменений. Тем не менее, мы знаем из опыта , что наличие FOR
, WHILE
и IF
доступно, даже если они просто синтаксический сахар для условных GOTO
моделей выражения нашего намерения легче .
Дело в том, что разные языки имеют разные особенности, которые облегчают или усложняют процесс выражения разного подхода к проблеме. И некоторые люди могут найти один способ выразить свое намерение легче, а другие - другим.
Пример, который я нашел в теге Ruby на StackOverflow: многие пользователи, которые следуют тегу Ruby, утверждают, что циклы легче понять, чем рекурсию, и рекурсия предназначена только для опытных функциональных программистов, а циклы более интуитивны для новичков, но я видел несколько случаев полные новички, которые интуитивно пишут такой код:
def rock_paper_scissors
get_user_input
determine_outcome
print_winner
rock_paper_scissors # start from the top
end
Что обычно приводит к тому, что несколько человек комментируют, что «это не работает» и «они делают это неправильно», а «правильный путь» таков:
def rock_paper_scissors
loop do
get_user_input
determine_outcome
print_winner
end
end
Итак, очевидно, что есть люди, для которых хвостовая рекурсия является более естественным способом выражения концепции «зацикливания», чем конструкции цикла.
Резюме
Тот факт, что два языка эквивалентны по Тьюрингу, говорит об одном и том же: они могут вычислять тот же набор функций на натуральных числах, что и машина Тьюринга. Вот и все.
Это ничего не говорит о том, как быстро они вычисляют эти функции. Это ничего не говорит о простоте выражения этих функций. И это ничего не говорит о том, что они могут делать, кроме вычисления функций на натуральных числах (например, ссылки на библиотеки C, чтение ввода от пользователя, запись вывода на экран).
Означает ли это, что класс проблем, которые каждый язык программирования может решать, зависит от языка, хотя все эти языки завершены?
Да.
- Существуют проблемы, которые не охватываются термином «полный Тьюринг» (который касается только вычислительных функций на натуральных числах), например печать на экран. Два языка могут быть полными по Тьюрингу, но один может разрешить печать на экране, а другой - нет.
- Даже если оба языка могут решать одни и те же проблемы, это ничего не говорит о том, насколько сложна кодировка и насколько легко выразить эту кодировку. Например, C может решить любую проблему, которую может Haskell, просто написав интерпретатор Haskell на C ... но сначала вы должны написать интерпретатор Haskell, чтобы решить проблему таким образом!