Те, що ви, по суті, запитуєте, - це різниця між обчислювальною потужністю і тим, що зазвичай називають виражаючою силою (або просто виразністю ) мови (або системою обчислення).
Обчислювальна потужність
Обчислювальна потужність ставиться до того , які проблеми мови можна обчислити. Найвідоміший клас обчислювальної потужності - це той, що еквівалентний Універсальній машині Тьюрінга . Є багато інших систем обчислень, таких як Random Access машини , l-числення , SK комбінатора обчислення , М-рекурсивної функції , WHILE
програми та багато іншого. І як виявляється, всі вони можуть імітувати один одного, а це означає, що всі вони мають однакову обчислювальну силу.
Це породжує тезу Церкви Тюрінга (названа на честь Церкви Алонцо, яка створила λ-числення, та Алана Тьюрінга, який створив Універсальну машину Тюрінга). Теза Церкви-Тьюрінга - це гіпотеза про обчислюваність з двох аспектів:
- всі обчислювальні системи, здатні до загальних обчислень, однаково потужні;
- людина, що дотримується алгоритму, може обчислити саме ті функції, які може обчислити машина Тьюрінга (і, отже, будь-яка з інших систем).
Однак друге важливіше в галузі філософії розуму, ніж інформатика.
Тим НЕ менше, є дві речі , Черча-Тьюринга-Thesis НЕ кажуть, що мають безпосередній стосунок до вашого запитання:
- наскільки ефективними є різні симуляції та
- наскільки зручним є кодування проблеми.
Простий приклад для (1): на машині з випадковим доступом копіювання масиву вимагає часу, пропорційного довжині масиву. На машині Тьюрінга, однак, потрібен час, пропорційний квадрату довжини масиву, оскільки машина Тьюрінга не має доступу до оперативної пам'яті, вона може переміщатися по стрічці по одній осередку за часом. Тому для їх копіювання потрібно перемістити по n елементів масиву n разів. Отже, різні моделі обчислень можуть мати різні характеристики продуктивності, навіть в асимптотичному випадку, де ми намагаємось абстрагуватися від деталей реалізації.
Прикладів для (2) є багато: і λ-обчислення, і Python є повним Тьюрінгом. Але чи бажаєте ви написати програму на Python чи на λ-обчисленні?
Є ще третя зморшка, яку я до цього часу обійшов: усі ці оригінальні системи були розроблені логіками, філософами або математиками, а не комп'ютерними науковцями ... просто тому, що комп'ютерів і, таким чином, інформатики не існувало. Всі вони йдуть назад до початку 1930 - х років, ще до Конрада Цузе «S дуже перші експерименти (що не були програмованими і / або Тьюрингу в будь-якому випадку). Вони говорять лише про "обчислювані функції на натуральних числах".
Зараз, як виявляється, є багато чого можна виразити як функції на натуральних числах - адже наші сучасні комп’ютери навіть обходять набагато менше, ніж це (в основному 3-4 функції на числах 0 і 1, і це все ), але, наприклад, яку функцію обчислює операційна система?
Це поняття вводу / виводу, побічних ефектів, взаємодіючих з навколишнім середовищем, не охоплює ідея "функції над натуральними числами". І все-таки, це наче важливо, оскільки, як одного разу сказав Саймон Пейтон Джонс "Вся чиста функція без побічних ефектів, це зробити ваш процесор гарячим" , на що учасник аудиторії відповів "насправді, це сторона -ефект теж! "
Едвін Брейді , дизайнер Ідріса , (лише половина) жартома використовує (не знаю, чи він його винайшов) терміном "Тетріс-повний", щоб виразити цю різницю між "може обчислити будь-яку обчислювальну функцію на натуральних числах" і "може використовувати для написання нетривіальних програм, які взаємодіють із середовищем ". Ще іронічніше він демонструє це, здійснюючи клон космічних загарбників в Ідрісі , але він говорить, що впевнений, що Тетріс зводиться до космічних загарбників.
Ще одна річ , щоб вказати на те , що не тільки є Тьюринг-еквівалентність не обов'язково достатньо , щоб говорити про фактично писати «корисні» програми, вона може OTOH також навіть не бути necesssary . Наприклад, SQL став еквівалентом Тьюрінга лише ANSI SQL: 1999 , але до цього він був корисний. Насправді, деякі можуть стверджувати, що створення Тьюрінга-еквівалента зовсім не додало його корисності. Існує багато мов, орієнтованих на домен, які не є еквівалентними Тьюрінгу. Мова опису даних зазвичай не є (і не повинна бути). Загальні мови, очевидно, не можуть бути еквівалентними Тьюрінгу, але ви все одно можете писати в них петлі подій, веб-сервери чи операційні системи. Є також мови, які є еквівалентом Тьюрінга, але там, де це насправді вважається помилкою.
Отже, загалом, еквівалентність Тьюрінга не є надзвичайно цікавою, якщо ви не хочете статично аналізувати програми.
Виразність
Якщо припустити, що наша обчислювальна система є достатньо потужною для обчислень, щоб навіть взагалі вирішити нашу проблему, що ми маємо зробити далі, - це висловити наш алгоритм вирішення цієї проблеми в якомусь формальному позначенні для цієї системи. Іншими словами: нам потрібно написати програму якоюсь комп'ютерною мовою. Ось тут і входить поняття виразності .
Це, по суті, стосується того, як "легко" або "приємно" писати нашу програму на нашій конкретній мові програмування. Як бачите, поняття досить розпливчасте, суб'єктивне та більше психологічне, ніж технічне.
Однак є спроби більш точних визначень. Найвідоміший (і найсуворіший із мене, про який я знаю) - Маттіас Феллейзен у своїй роботі « Про виразну силу мов програмування (перші дві сторінки містять ніжне вступ, решта статті - м'ясніша»).
Основна інтуїція така: під час перекладу програми з мови на іншу мову деякі зміни, які потрібно внести, містяться локально (наприклад, перетворення 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 може вирішити кожну проблему. Haskell може, просто написавши інтерпретатора Haskell в C ... але для початку вирішити проблему потрібно спочатку написати інтерпретатора Haskell.