Невелика мова, схожа на С, яку можна моделювати машинами


11

Я шукаю невелику мову, яка допомагає «переконати» учнів у тому, що тюрінг-машини є достатньо загальною обчислювальною моделлю. Тобто мова, схожа на мови, до яких вони звикли, але також легко імітувати на машині тюрінга.

Papadimitriou використовує машини для оперативної пам’яті для цієї роботи, але я побоююся, що порівняння чогось дивного (як машина для твердження) з іншою дивною річчю (в основному, мовою складання) було б занадто непереконливим для багатьох студентів.

Будь-які пропозиції будуть дуже вітаються (особливо, якщо вони прийшли з деякою рекомендованою літературою)


7
Існує причина, що комп’ютери спочатку запрограмовані мовою асемблери ... написання компіляторів чи інтерпретаторів не є дрібницею . А писати компілятори чи перекладачі для машин Тьюрінга, напевно, ще важче.
Пітер Шор

Потрібно дещо не погоджуватися з PS, компілятор TM не є набагато складнішим, ніж, наприклад, перетворення екземплярів факторингу в SAT або інші майже студентські вправи. дивіться також топ-тренажери для тюрінгу в Інтернеті . ось приклад компілятора машини Тьюрінга, написаного в рубіні з зразком вихідного коду (для мови високого рівня). на жаль, там, здається, немає більш відполірованих. це зробить чудовий проект з відкритим кодом.
vzn

2
@OmarShehab, Редагування запитує питання на першу сторінку. Будь ласка, не редагуйте старе питання, коли редагування не суттєво покращує питання. Також редагування великої кількості запитань, які не є на першій сторінці, не є корисним, оскільки воно виштовхує нові запитання з першої сторінки. Дякую.
Каве

@kaveh зрозумів.
Омар Шехаб

Відповіді:


15
  • Якщо ваші студенти виконали будь-яке функціональне програмування, найприємніший підхід, який я знаю, - почати з нетипового обчислення лямбда, а потім використовувати теорему абстракції дужок, щоб перевести її в комбінатори SKI. Потім ви можете використовувати теореми та щоб показати, що машини Тьюрінга утворюють часткову комбінаторну алгебру , і так можна інтерпретувати комбінатори SKI.u t msmnutm

    Я сумніваюся, що це найпростіший можливий підхід, але мені подобається, як він спирається на деякі найбільш фундаментальні теореми в обчислюваності (які ви, можливо, хочете висвітлити з інших причин).

    Схоже, Андрій Бауер відповів на подібне запитання щодо Mathoverflow кілька місяців тому.

  • Якщо ви встановлені на мові, подібній С, ваш шлях буде набагато більш чітким, оскільки вони мають досить складну семантику - вам потрібно буде

    1. Покажіть, що машини Тьюрінга можуть одночасно імітувати стек та купу, і
    2. Покажіть, як змінні можна реалізувати за допомогою стека та
    3. Покажіть, що виклики процедур можуть бути реалізовані зі стеком.

    Це велика частина змісту класу компіляторів, якщо чесно.


7

Моя теорія професора Comp в університеті почалася з доказу, що односмугова машина Тьюрінга може реалізувати багатосмугову машину Тюрінга. Це обробляє декларацію змінної: якщо програма має шість оголошень змінної, то її можна легко реалізувати на семикосмічній машині Тюрінга (стрічці для кожної змінної та стрічці "зареєструвати", щоб допомогти виконувати такі завдання, як арифметика та перевірка рівності між стрічки). Потім він показав, як реалізувати базові петлі FOR і WHILE, і в цей момент у нас з'явилася основна мова, схожа на Turing, на C. Я вважаю, що це все задовольняє.


6

Зараз я думаю про те, як переконати себе, що машини Тьюрінга є загальною моделлю обчислень. Я погоджуюсь, що стандартне трактування тези Церкви Тьюрінга в деяких стандартних підручниках, наприклад, Сіпсера, не дуже повно. Ось ескіз того, як я можу перейти від машин Тьюрінга до більш впізнаваної мови програмування.

Розгляне блок-структурована мова програмування з ifі whileзаяву, з НЕ-рекурсивними певними функціями і підпрограмами, з іменованими булеві випадковими величинами і загальними логічними виразами, і з однієї необмеженим булевим масивом tape[n]з покажчиком масиву цілого , nяке може бути збільшена або зменшена, n++або n--. Вказівник nспочатку дорівнює нулю, а масив tapeспочатку є рівним нулю. Так, ця комп’ютерна мова може бути схожою на C або Python, але вона дуже обмежена у своїх типах даних. Насправді вони настільки обмежені, що у нас навіть немає способу використовувати вказівник nу булевому виразі. Якщо припустити, щоtapeє нескінченним праворуч, ми можемо оголосити підводний підтік "системною помилкою", якщо nвін коли-небудь негативний. Крім того, наша мова має exitвисловлювання з одним аргументом, щоб виводити булеву відповідь.

Тоді першим моментом є те, що ця мова програмування є хорошою мовою специфікації для машини Тьюрінга. Ви легко бачите, що, за винятком стрічкового масиву, код має лише кінцево багато можливих станів: стан усіх заявлених змінних, поточний рядок виконання та стек підпрограми. Останній має лише обмежений стан, оскільки рекурсивні функції заборонені. Ви можете уявити собі "компілятор", який створює "фактичну" машину Тьюрінга з коду такого типу, але деталі цього не важливі. Справа в тому, що у нас мова програмування з досить хорошим синтаксисом, але дуже примітивними типами даних.

Решта конструкції полягає в перетворенні цього на більш зручну мову програмування з кінцевим списком функцій бібліотеки та етапів попередньої компіляції. Ми можемо діяти так:

  1. За допомогою прекомпілятора ми можемо розширити булеві типи даних до більшого, але кінцевого алфавіту символів, такого як ASCII. Можна припустити, що tapeприймає значення в цьому більшому алфавіті. Ми можемо залишити маркер на початку стрічки, щоб запобігти перетіканню вказівника, і рухомий маркер в кінці стрічки, щоб запобігти випадковому ковзанню ТМ до нескінченності на стрічці. Ми можемо реалізовувати довільні бінарні операції між символами та перетвореннями на булі для ifта whileоператорів. (Насправді це ifможе бути реалізовано whileтакож, якщо воно не було доступне.)

  2. Ми хочемо необмежений цілий тип даних для того, щоб реалізувати як випадковий доступ до стрічки, так і (позитивну) цілу арифметику. З цією метою ми моделюємо стрічку TM для деякого фіксованого однією стрічкою, яку ми маємо. Ця побудова дана як теорема Шипсера. Ідея полягає в тому, щоб переплетені емульовані стрічки на низькорівневій стрічці у нас із маркерними символами, що представляють положення голови. Якщо покажчик стрічки низького рівня дорівнює нулю, він обслуговує й піддіапазон, переміщуючись у положення а потім стрибаючи кроків за один раз; після кожного читання чи запису низький рівень покажчика стрічки зменшується до нуля. Як і на попередньому етапі, це легше реалізувати як попередній компілятор.k i i kkkiik

  3. Одну стрічку ми позначаємо як "пам'ять", а іншу - "регістри" або "змінні", які не мають підпису, цілими значеннями. Ми зберігаємо цілі числа в двійковій системі малої ендіану з маркерами закінчення. Спочатку реалізуємо копію реєстру та бінарний декрет регістра. Поєднуючи це з збільшенням і зменшенням покажчика пам'яті, ми можемо здійснити пошук випадкового доступу до пам'яті символів. Ми також можемо записати функції для обчислення двійкового додавання та множення цілих чисел. Не важко записати двійкову функцію додавання з побітними операціями, а функцію помножити на 2 при зсуві вліво. (Або дійсно правильний зсув, оскільки це малоеквієнт.) За допомогою цих примітивів ми можемо записати функцію множення двох регістрів, використовуючи довгий алгоритм множення.

  4. Ми можемо реорганізувати стрічку пам'яті з одновимірного масиву символів symbol[n]у двовимірний масив символів symbol[x,y]за допомогою формули n = (x+y)*(x+y) + y. Тепер ми можемо використовувати кожен рядок пам'яті для вираження безпідписаного цілого числа у двійковій формі із символом закінчення, для отримання одновимірної пам'яті з цілим значенням у випадковому доступі memory[x]. Ми можемо реалізувати читання з пам'яті в цілий регістр і запис з регістра в пам'ять. Зараз багато функцій можна реалізувати за допомогою функцій: арифметика з підписами та плаваючою точкою, рядки символів тощо.

  5. Тільки ще один базовий інструмент суворо вимагає докомпілятора, а саме рекурсивних функцій. Це можна зробити за допомогою методики, яка широко використовується для реалізації інтерпретованих мов. Кожній рекурсивній функції високого рівня ми призначаємо рядок імен і організовуємо код низького рівня в один великий whileцикл, який підтримує стек виклику із звичайними параметрами: точка виклику, викликана функція та список аргументів.

На даний момент конструкція має достатньо функцій мови програмування високого рівня, що подальша функціональність - це більше тема мов програмування та компіляторів, а не теорія CS. Так само вже легко написати тренажер машини Тюрінга на цій розробленій мові. Написати самокомпілятор для мови не просто, але, звичайно, стандартно. Звичайно, вам потрібен зовнішній компілятор, щоб створити зовнішній ТМ з коду на цій мові, подібній С або Python, але це можна зробити на будь-якій мові комп'ютера.

Зауважимо, що ця замальована реалізація підтримує не лише тезу Церкви-Тьюрінга логіків для рекурсивного функціонального класу, але й розширену (тобто поліноміальну) тезу Церкви-Тьюрінга, як це стосується детермінованих обчислень. Іншими словами, він має многочлен. Насправді, якщо нам дають машину оперативної пам’яті або (мій особистий фаворит) ТМ-дерево, це може бути зведено до полілогоарифмічних накладних витрат для послідовних обчислень з оперативною пам’яттю.


5

Компілятор LLVM дозволяє досить просто «підключити» нову архітектуру. Вони називають це написання новим бек-ендом і дають докладні вказівки та приклади, як це зробити. Я підозрюю, що вам доведеться перестрибувати деякі обручі щодо пам'яті випадкового доступу, якщо ви не хочете орієнтуватися на машину Тьюрінга ОЗУ, але це, безумовно, можливо, оскільки я бачив ряд проектів, які спричиняють генерацію LLVM VHDL або інші дуже різні мови машин.

Це призведе до цікавого ефекту від наявності сучасного оптимізуючого компілятора (багато в чому LLVM є більш досконалим, ніж GCC), що генерує код для машини Тьюрінга.


1

Я не в теорії cs, але в мене є щось, що може бути корисним. Я взяв ще одне схвалення. Я сконструював простий процесор, який програмується безпосередньо з невеликим підмножиною C. Немає асемблерного коду, лише C-подібний код. Ви можете використати той самий інструмент, який я використовував і модифікувати цей процесор, щоб створити ваш тренажер машини Тьюрінга. На розробку, моделювання та тестування цього процесора знадобилось 4 дні, і кілька інструкцій! Інструменти, якими я користувався, навіть дозволили мені створити реальний синтезуючий код VHDL. Це справжній робочий процесор.

Ось як виглядає програма: Приклад С-подібної програми складання

Ось зображення процесора з використанням тези Інструменти: Схема процесора

Інструменти "Novakod Studio" використовує мову опису апаратних засобів високого рівня. Як приклад, ось код лічильника програми: psC - паралельний та синхронний зразок коду C Досить говорити, якщо хтось зацікавлений, ось публічна інформація, щоб зв’язатися зі мною: https://repertoire.uqac.ca/Fiche.aspx?id=JjstNzsH0&link=1

Люк


чи використовує адресація пам'яті фіксовану кількість бітів для пошуку адрес?
vzn

Так, але змінити розмір пам'яті просто (int DataMemory [SIZE]). Мова підтримує ціле число змінної довжини (int: 10). Але, оскільки вона націлена на FPGA, масив є статичним і постійним розміром.
Luc Morin

1

Як щодо прийняття тут ідеї, представленої користувачем GMB (машина Тюрінга з однією стрічкою може імітувати машину Тюрінга з N стрічками, переплітаючи N стрічок на одну стрічку і читаючи будь-яку з цих стрічок, стрибаючи з N місць за один раз, Тьюрінг Машина з N стрічками може реалізувати ...) і написати програму машини Тьюрінга, яка реалізує спрощену RAM-машину. RAM-машина може бути насправді спрощеним, реальним процесором з наявними резервними копіями LLVM або GCC. Тоді GCC / LLVM може бути використаний для перехресного компіляції програми C для цього процесора, а програма машини Тьюрінга, яка імітує RAM-машину, запускає RAM-машинне моделювання, маючи змодельовану RAM-машину виконати вихід GCC / LLVM. Реалізація машини Тьюрінга може бути дуже простим кодом С, який підходить до невеликого файлу C.

Що стосується ОЗУ-машини, то існує демо-проект, де 32-бітний процесор моделюється 8-бітовим мікроконтролером, а модельований 32-бітний процесор завантажує Linux. Повільно як пекло, але за словами автора Дмитра Грінберга, це спрацювало. Можливо, процесор Zylin (користувач GitHub zylin) може бути життєздатним вибором для модельованої RAM-машини. Іншим кандидатом на автомат RAM може бути крапка ProjectOberon від Niklaus Wirth .

("Точка" та "ком" у моєму тексті пояснюються тим, що я щойно 2015_10_21 зареєстрував свій акаунт на сайті cstheory.stackexchange, а веб-додаток не допускає більше 2 посилань для початківців користувачів, незважаючи на те що вони можуть автоматично бачити з моїх інших облікових записів stackexchange, що я можу бути дурним, але я не троль.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.