Найменше (чіткі) символи для повноти Тьюрінга


107

Підсумок:

Що стосується будь-якої мови, яка найменша кількість унікальних символів для вашої мови має бути Тюрінг-завершеною ?

Виклик:

Для будь-якої мови на ваш вибір знайдіть найменший набір символів, який дозволяє вашій мові бути Turing-Complete. Ви можете повторно використовувати набір символів стільки разів, скільки вам захочеться.


Приклади:

  • JavaScript: +!()[]( http://www.jsfuck.com )

  • Brainfuck: +<>[](передбачає обертовий розмір комірки)

  • Python 2: ()+1cehrx(зроблений з таких сценаріїв exec(chr(1+1+1)+chr(1)))

Оцінка:

Цей виклик оцінюється символами , а не байтами . Наприклад, Оцінки для прикладів становлять 6, 5 та 9.


Примітки:

  • Цей виклик відрізняє від інших тим, що ти лише твою мову є Тюрінг-повною (не обов'язково вміти використовувати кожну функцію мови).

  • Хоча ви можете, будь ласка, не публікуйте відповідей, не скорочуючи використаних символів. Приклад: Brainfuck з 8 символами (оскільки кожен інший символ є коментарем за замовчуванням.)

  • Ви ОБОВ'ЯЗКОВЕ надати хоча б коротке пояснення того, чому ваш підмножина є Тюрінг-завершеним.


90
Одинарний , 1 персонаж. зітхає
Денніс

4
@Dennis Це не так відрізняється від Jelly або 05AB1E, який має вбудовану цікаву проблему з теорії чисел. Ця проблема все ще здається цікавою і нетривіальною проблемою оптимізації будь-якою мовою, яка не була створена як тарпіт.
Мартін Ендер

7
@MartinEnder Мені б особливо цікаво побачити відповіді на таких мовах, як Java або C.
Julian Lachniet

9
Будь ласка, не публікуйте рішення в esolangs, де рішення є кожним дійсним символом на мові. Це не цікаво чи розумно.
Павло

20
@Pavel Не цікавий або розумний може означати, що він не повинен бути оприлюдненим, але, звичайно, не повинен публікувати.
Денніс

Відповіді:


77

Хаскелл, 4 ч

()=;

За допомогою ()=нас ми можемо визначити S, K і I. Визначення повинні бути розділені ;або через NL, або на NL.

Ми визначаємо (==)як S (другий рядок показує більш читану версію):

((=====)==(======))(=======)=(=====)(=======)((======)(=======))
( a     == b      ) c       = a      c       ( b       c       )

(===) як K:

(=====)===(======)=(=====)
 a     === b      = a

і (====)як я:

(====)(=====)=(=====)
(====) a     = a 

На щастя (==), (===), (====)і т.д. є допустимими іменами функцій / параметрів.

Як зазначає @ ais523, SKI недостатньо в такій сильно набраній мові, як Haskell, тому нам потрібно додати комбінатор точок фіксації (=====):

(=====)(======)=(======)((=====)(======))
(=====) f      = f      ((=====) f      )

17
Ця конструкція не працює безпосередньо; SKI не завершують Тюрі на сильно набраній мові, як Haskell. Однак я вважаю, що ви можете використовувати ту саму методику, щоб визначити fix, і SKI + fix є Тюрінг завершеним, навіть сильно набраною мовою.

О, значить, ви префіксуєте ці визначення на початку кожної програми?
PyRulez

@PyRulez: так. Згідно з нашими дефолтами я вважаю, що достатньо, щоб можна було побудувати функції з заданим набором символів - повна програма не потрібна.
німі

1
Ви, ймовірно, повинні замінити, (==)щоб він не зіткнувся з оператором рівності за замовчуванням
гордий haskeller

@proudhaskeller: так, якщо ви насправді хочете програмувати, то краще було б перейменувати (==), але наведений вище код - лише доказ цілісності повноти.
німі

61

JavaScript (ES6), 5 символів

Дякуємо @ETHproductions та @ATaco за допомогу в цьому; це був груповий проект, і хоча початкова ідея була моєю, багато деталей є їхніми. Дивіться дискусію в чаті, де була розроблена ця підмножина JavaScript тут .

[]+=`

Досить добре встановлено, що будь-яку програму Javascript можна писати символами ( []()+!), але 5 символів недостатньо . Однак це не є проблемою щодо написання довільного JavaScript. Це виклик написання мови, що повністю закінчується Тюрінгом, і використання того факту, що мовам повного Тьюрінга не потрібен доступ до DOM або навіть інтерактивного вводу / виводу, виявляється, що ми можемо написати програму з усіма необхідними функціональними можливостями. , навіть не маючи можливості виконувати evalабо еквівалент.

Основні завантажувальні програми

JavaScript дуже гнучкий з типами. Наприклад, []це порожній масив, але +[]дорівнює 0, і []+[]це нульовий рядок. Примітно, той факт, що ми можемо створити 0 за допомогою цього набору символів, дозволяє моделювати ефект круглих дужок для групування; (a)можна записати як [a][+[]]. Ми можемо використовувати цей вид хитрощів для створення різних символів, використовуючи лише +[]:

  • [][+[]]є undefined(будучи першим елементом порожнього масиву); тому
  • []+[][+[]]є "undefined"(розшарування undefined); тому
  • [[]+[][+[]]]є ["undefined"](загортання в масив); тому
  • [[]+[][+[]]][+[]]є "undefined"(його перший елемент); тому
  • [[]+[][+[]]][+[]][+[]]є "u"(його перша літера).

uє одним з найпростіших персонажів для створення, але подібні методи дозволяють нам створити ряд інших персонажів. Це ж посилання, що було раніше, дає нам наступний список символів, доступ до яких +[](це той самий список, що і для +[](), за винятком, ,тому що це єдина конструкція, яка використовує круглі дужки для інших цілей, ніж групування / пріоритет):

0123456789acdefinotuvyIN (){}.

З цього набору символів ми не можемо написати дуже багато корисних слів (пам’ятайте, що це набір символів, який ми можемо створити як рядки ; нам не вдасться їх виконати без якогось роду eval). Як такий, нам потрібен інший персонаж. Ми будемо використовувати =, тому що вона стане корисною пізніше, але поки що ми будемо використовувати її для написання оператора порівняння ==. Це дозволяє нам виробляти falseі true, які можна впорядкувати та індексувати, і негайно додамо lrsдо символів, які ми можемо включити до рядків.

На сьогоднішній день, найважливіше слово, яке це дає нам змогу писати, чого ми не могли раніше, - це constructor. Тепер у JavaScript є синтаксис доступу до властивості, який виглядає приблизно так:

object.property

але ви також можете написати це так:

object["property"]

і ніщо не заважає нам використовувати обчислену властивість, а не буквений рядок. Таким чином, ми можемо щось робити за принципом

object["c"+"o"+"n"+"s"+"t"+"r"+"u"+"c"+"t"+"o"+"r"]

(з літерами, згенерованими як описано вище; код швидко стає дуже довгим!); це рівнозначно object.constructor, що дозволяє нам отримувати доступ до конструкторів довільних об'єктів.

Є кілька хитрощів, які ми можемо зробити з цим. Від мирського до фантастичного:

  • Конструктор об’єкта - це функція. Зокрема, воно має ім'я, і ​​це ім'я є частиною строфікації функції. Так, наприклад, ми можемо зробити, [[]+[]][+[]]["constructor"]щоб отримати в конструкторі рядок, ім'я якого - String, а потім поменшити його для отримання Sсимволу великої літери. Це трохи розширює наш алфавіт, і нам знадобляться деякі нові символи пізніше.
  • Усі масиви мають однаковий конструктор; []["constructor"] == []["constructor"]є true(на відміну від [] == [], що помилково). Це може здатися незначним, але це дуже важливо, оскільки це дає нам метод постійного зберігання значень; ми можемо встановити випадкову властивість на конструкторі та прочитати його пізніше. (Це одна з причин, яку ми використовуємо =зокрема, а не один із інших способів генерування trueта false.) Наприклад, ми можемо оцінити [[]["constructor"]["a"]=[]], а згодом прочитати []["constructor"]["a"]та повернути той самий масив, який ми зберігали там.

    Це відповідає одній з вимог, необхідних нам для Тьюрінга-повноти , можливості зберігання та отримання довільних обсягів даних. Ми можемо створити клітинку мінусів за допомогою двоелементного масиву, взявши значення з нашого сховища конструктора-властивостей, а потім зберегти його замість одного з цих значень, дозволяючи нам створювати в пам'яті довільно великі структури даних; і ми можемо отримати доступ до цього сховища, зробивши зворотній бік, відриваючи його по частинах, поки потрібні нам дані не стануть доступними. Читання є руйнівним, але це прийнятно, оскільки у нас є більше місця для зберігання даних, тому ми можемо скопіювати їх під час читання, а потім повернути копію назад у вихідне місце.

  • Це дозволяє нам отримати в конструкторі функцію (є багато функцій, до яких ми можемо отримати доступ з обмеженим алфавітом; []["find"]наприклад, Array.find - це найпростіший доступ, але є й інші). Чому це корисно? Що ж, ми можемо насправді використовувати його за призначенням конструктора та конструювати функції! На жаль, за допомогою нашого набору символів ми не можемо передати обчислюваному рядку конструктору функцій. Однак використання дій `дозволяє передати це рядковому буквальному (наприклад []["find"]["constructor"]`function body goes here`); це означає, що ми можемо визначити власні значення типу функції з будь-якою поведінкою при виконанні, доки ми можемо висловити цю поведінку цілком, використовуючи []+=. Наприклад, []["find"]["constructor"]`[]+[]`це досить нудна функція, яка обчислює нульову рядок, відкидає її та виходить; щоФункція не корисна, але складніші будуть. Зауважте, що хоча функції не можуть приймати параметри або повертати значення, це не є проблемою на практиці, оскільки ми можемо використовувати сховище властивостей конструктора для спілкування від однієї функції до іншої. Ще одне обмеження полягає в тому, що ми не можемо використовувати `в тілі функції.

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

    Ми виконуємо це досить витонченою хитрістю. Пам’ятаєте капітал, який Sми генерували раніше? Це дозволяє нам заклинання "toString". Ми не збираємось називати це; ми можемо конвертувати речі в рядки, додаючи []їх. Швидше, ми збираємось його замінити . Ми можемо використовувати зберігання в конструкторі для визначення стійких масивів, які тримаються навколо. Потім ми можемо призначити створені функції toStringметодам масивів , і ці завдання також будуть триматися. Тепер все, що нам потрібно зробити, це простий +[]масив, і раптом наша визначена на замовлення функція запуститься. Це означає, що ми можемо використовувати символи+=[]викликати функції, і тому наші функції можуть дзвонити один одному - або самі. Це дає нам рекурсію, яка дає нам петлі, і раптом у нас є все необхідне для Тюрінга-повноти.

Ось перелік набору функцій, які надають Тьюрінгу повноту та як вони реалізовані:

  • Без обмеженого сховища : вкладені масиви в сховищі конструктора
  • Контрольний потік : реалізований за допомогою ifта рекурсії:
    • if: перетворити булеве число у число та індексувати у 2-елементний масив; один елемент запускає функцію для thenвипадку, коли він є строковим, інший запускає функцію для elseвипадку, коли він є строковим
    • Рекурсія : підкресліть відповідний елемент сховища конструктора
  • Послідовність команд : [a]+[b]+[c]оцінює a, bі cзліва направо (принаймні, у браузері, який я перевірив)

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


16
Якщо це не названо, я пропоную J5h * t.
CalculatorFeline

1
Якою може бути хороша прикладна програма? Прем'єр-тест? Привіт Світ?
CalculatorFeline

3
Моя, це так ват ... смачна відповідь, як хороший фільм жахів.
перестали повертати проти годинника,

4
Я вважав, що використання Angular1 toString()для введення залежностей є найбільш творчим способом використання функції. Тепер я передумав.
Сонячний Pun

1
Ось приклад: pastebin.com/QGbjmB5Y
Esolanging Fruit

55

Одинарний , 1 персонаж

0

Вибір персонажа насправді не має значення; тривалість програми визначає мозкову програму, в яку вона перекладається. Хоча специфікація наказує 0символи, більшість транспіляторів, схоже, не перевіряють цього.


44
Ми, мабуть, повинні відкрити проблеми з транспіляторами, що підтверджують специфікацію, це дуже серйозне питання.
Людина капітана

5
Я приголомшений. Мені знадобилося 20 хвилин, щоб сказати, чи це жарт.
Пітер А. Шнайдер

3
@ PeterA.Schneider Дехто з googling виявить, що хтось насправді реалізував цю теорію таким чином теоретично, хоча результуюча рядок 0 є, можливо, найбільшою кількістю, яку я бачив у будь-якому практичному контексті, і ніколи не може бути реалізована на реальній машині.
Даррен Рінгер

12
Цей рядок нулів насправді є найменшою кількістю, яку я бачив у будь-якому контексті.
Метью Прочитайте

1
LOL, добре, якщо ви зробите щось нерозумне, як визначення свого єдиного символу як добавки ідентичності ...: p
Даррен Рінгер

37

vim, 9 8 7 6 символів

<C-v><esc>1@ad

Ми можемо створити та виконати довільну програму vimscript наступним чином:

  1. Використовуйте послідовність aa<C-v><C-v>1<esc>dd@1<esc>ddddдля отримання <C-a>в регістрі 1.

  2. Увійдіть у режим вставки з a, а потім вставіть антену a, яка буде використана для повторного введення режиму вставки в макрос пізніше.

  3. Для кожного символу потрібної програми vimscript

    1. використовувати <C-v><C-v>1<esc>для вставки буквальної послідовності <C-v>1,

    2. використання @1(що є<C-a><cr> , коли фінал <cr>є необоротнім через те, що він знаходиться в останньому рядку) стільки разів, скільки потрібно для збільшення 1значення ASCII потрібного символу,

    3. і знову введіть режим вставки за допомогою a .

  4. Видаліть рядок (разом із кінцевою новою лінією) до 1реєстру за допомогою <esc>dd.

  5. Виконайте результат як натискання клавіш vim, використовуючи @1, а потім <esc>ddдля видалення рядка, введеного останнім новим рядком з попереднього кроку.

  6. Запустіть отриману довільну послідовність байтів за допомогою dd@1. Якщо він починається з :символу a , він буде інтерпретуватися як код вимскрипту, і він буде запущений завдяки новому рядку, що відкладається dd.

Я не переконаний, що це мінімальний набір символів, але довести, що він є Тьюрінгом, досить легко.


2
Чи не можете ви i<C-v>1<ESC>написати, <C-a>а потім ddтак, щоб ви могли використовувати @1для збільшення будь-яких чисел і внаслідок цього не потрібно використовувати <C-a>?
Корови кракають

4
О, ця відповідь неймовірна! +1!
DJMcMayhem

@KritixiLithos Це закінчилося після трохи реструктуризації, дякую!
Дверна ручка

2
@ mbomb007 Насправді ... завдяки цікавій деталі реалізації, <C-v>10вставляє NUL, а не \ n (не питати). У будь-якому випадку, так, це не має значення щодо повноти Тьюрінга.
Дверна ручка

1
Чи може бути коротше? golf.shinh.org/p.rb?Hello+broken+keyboard#Vim
mbomb007

33

Perl, 5 символів

<>^es

Як і в інших мовах сценаріїв, ідея полягає в тому, щоб eval довільних рядках. Однак наш набір символів не включає лапок або операторів конкатенації, тому побудова довільних рядків буде набагато складнішою. Зауважте, що eval^"було б набагато простіше впоратися, але він має ще один характер.

Наш головний інструмент - це те s<^><CODE>ee, що оцінюєтьсяCODE , а потім і оцінюється. Більше eможуть бути додані, з очікуваним ефектом.

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

Об’єднавши ці рядки разом, ми можемо отримати будь-яку комбінацію символів ^B^V^S(*-/9;<>HJMOY[`\^begqstv , якщо ми погодимось, що навколо є сміття (перші три з них - це контрольні символи).

Наприклад, припустимо, що ми хочемо отримати "v99". Один із способів отримати v99це "><<" ^ "e>>" ^ "see" ^ "^^^", але ми не можемо представити ці рядки через обмеження <>. Тому замість цього ми використовуємо:

<^<<^>><>>^<^^^^^<>>^<^^^^^^e>^<^^^^^^^>^<^^^^^e>^<^^^^e^>^<e^^es>^<^ee^^>^<^<^^^^^>>^<^<>^^^^>^<^^^^^^^e>^<^^^^^^^^>

Вищенаведені врожаї Y9;v99;, які при оцінці дають такий же результат, як і звичайнаv99 (а саме символ з кодом ASCII 99).

Таким чином, ми можемо використовувати весь ^B^V^S(*-/9;<>HJMOY[`\^begqstvдіапазон для створення нашого довільного рядка, а потім перетворити його, як зазначено вище, і вставити вs<><CODE>eeee для її виконання. На жаль, цей набір ще дуже обмежений, без явного способу проведення конкатенації.

Але, на щастя, вона містить зірку. Це дозволяє нам записати *b, що оцінює на рядок "*main::b". Тоді *b^<^B[MMH^V^SY>(^ B ^ V і ^ S є буквальними керуючими символами) дають нам (6, $&);, що, коли Eval-й вид знову повертає значення змінного відповідності в Perl, $&. Це дозволяє нам використовувати обмежену форму конкатенації: ми можемо неодноразово додавати речі до $_використання s<^><THINGS>e, а потім використовувати s<\H*><*b^<^B[MMH^V^SY>>eeeдля eval $_( \Hвідповідає будь-чому, окрім горизонтального пробілу; ми використовуємо його замість крапки, якої немає в нашому графіку).

Використовуючи 9-/, ми можемо легко генерувати всі цифри. Використовуючи цифри vта конкатенацію, ми можемо генерувати довільні символи (vXXX видає символ із кодом ASCII XXX). І ми можемо об'єднати їх, щоб створити довільні рядки. Так виглядає, що ми можемо зробити все, що завгодно.

Напишемо повний приклад. Припустимо, ми хочемо програму, яка друкує власний PID. Почнемо з природної програми:

say$$

Ми перетворюємо його в v-notation:

s<><v115.v97.v121.v36.v36>ee

Ми переписуємо це лише за допомогою ^B^V^S(*-/9;<>HJMOY[`\^begqstv(пробіл призначений лише для читання і не впливає на вихід):

s<^><
    s<^><9*9-9-9-9-9-9>e;
    s<^><v>;
    s<v\H\H><*b^<^B[MMH^V^SY>>eee;
    s<^><9*9-9-9-9-9-9>e;
    s<^><v>;
    s<v\H\H><*b^<^B[MMH^V^SY>>eee;
    s<^><99-99/-9-99/-9>e;
    s<^><v>;
    s<v\H\H\H><*b^<^B[MMH^V^SY>>eee;
    s<^><99-9/9-9/9>e;
    s<^><v>;
    s<v\H\H><*b^<^B[MMH^V^SY>>eee;
    s<^><999/9-9/-9-9/-9-9/-9-9/-9>e;
    s<^><v>;
    s<v\H\H\H><*b^<^B[MMH^V^SY>>eee;
    s<\H*><*b^<^B[MMH^V^SY>>eee;
>eee;

Нарешті, ми перетворюємо вищевказану програму лише на <>^es: pastebin . На жаль, це збій Perl Excessively long <> operator, але це лише технічне обмеження, і його не слід враховувати.

Фу, це була цілком подорож. Було б дуже цікаво побачити, як хтось придумав набір з 5 символів, що робить речі простішими.

EDIT: використовуючи дещо інший підхід, ми можемо уникати обмеження довжини <>. Повністю функціональний перекладач мозкових можливостей, використовуючи лише <>^es: Спробуйте в Інтернеті! . Автоматизований Perl для <>^esтранспілятора: пастбін .


1
Я бачу .. ваше кодування отримує квадратичний вигляд, тому що ваші персонажі діляться на дві групи, таку, яку можна створити лише шляхом набору парного числа базових символів, та іншу, яка може бути створена лише непарним числом, змушуючи вас додайте інший глобус, змінюючи між ними. Будь-який шанс, що ви могли б розділити програму на більш короткі оцінювані шматки, пов'язані разом з ^іншими базовими символами?
Ørjan Johansen

@ ØrjanJohansen Так, добре, помітивши це. Я зараз працюю над рішенням.
Grimy

Ви можете зробити цей стислий приклад посиланням TIO. Спробуйте його онлайн!
Ørjan Johansen

7
Запит: Поясніть цей "трохи інший підхід"
CalculatorFeline

32

Python 2, 7 символів

exc="%\n

Будь-яку програму Python 2 можна кодувати за допомогою цих 7 символів ( \nце новий рядок).

Побудова довільних рядків

Ми можемо виконати конкатенацію, повторно застосувавши оператор заміни %на одній строці. Наприклад, якщо a=1, b=2, c=3, "%d%%d%%%%d" % a % b % cдасть нам рядок "123". На щастя, літери execдають нам доступ до %xі %cякі в основному hex()і chr(). З %c, ми можемо побудувати будь-який рядок, якщо у нас є необхідні числа, які представляють символи. Потім ми можемо виконати цей рядок як код python за допомогою execключового слова.

Складіть числа

Ми можемо отримати доступ до кажана 0та 1одразу від нього, порівнявши ( ==). За допомогою комбінації сполучувальних цифр і модуля можна побудувати число, 43яке представлено +в ASCII. Це дозволяє нам побудувати числа, необхідні для нашого коду.

Збираючи його разом

У цьому поясненні я опустив декілька деталей, оскільки вони не є важливими для розуміння того, як можна записати програми в рамках цих обмежень. Нижче представлена ​​програма Python 2, про яку я писав, що перетворює будь-яку програму python у функціонально еквівалентну версію, яка використовує лише ці 7 символів. Використовувані методи натхненні цим поданням про анархічний гольф від k. Деякі прості хитрощі також використовуються, щоб утримати розмір створених програм у розумних межах.

import sys

var = {
    43: 'e',
    'prog': 'x', # the program will be stored in this variable
    'template': 'c',
    0: 'ee',
    1: 'ex',
    2: 'ec',
    4: 'xe',
    8: 'xx',
    16: 'xc',
    32: 'ce',
    64: 'cc',
    'data': 'cx', # source program will be encoded here
}

unpacker = 'exec"".join(chr(eval(c))for c in {}.split())'.format(var['data'])

source = sys.stdin.read()
charset = sorted(list(set(source+unpacker)))
codepoints = map(ord, charset)

output = (
    # create template for joining multiple characters
    '{}="%c%%c%%%%c%%%%%%%%c"\n'.format(var['template']) +

    # create 1
    '{0}={1}=={1}\n'.format(var[1], var['template']) +

    # create 0
    '{}={}==""\n'.format(var[0], var['template']) +

    # create 3
    # store it at var[43] temporarily
    (
        'exec"{0}=%x%%x"%{2}%{2}\n' +
        'exec"{0}%%%%%%%%=%x%%x%%%%x"%{1}%{2}%{1}\n'
    ).format(var[43], var[0], var[1]) +

    # create 4
    # this step overwrites the value stored at var[0]
    (
        'exec"{1}=%x%%x"%{0}%{1}\n' +
        'exec"{1}%%%%=%x%%x"%{2}%{0}\n'
    ).format(var[43], var[0], var[1]) +

    # create 43
    'exec"{0}=%x%%x"%{1}%{0}\n'.format(var[43], var[0])
)

# create powers of 2
for i in [2, 4, 8, 16, 32, 64]:
    output += 'exec"{0}={1}%c{1}"%{2}\n'.format(var[i], var[i/2], var[43])

for i, c in enumerate(codepoints):
    # skip if already aliased
    if c in var:
        continue

    # generate a new name for this variable
    var_name = ''
    if i < 27:
        for _ in range(3):
            var_name += 'exc'[i%3]
            i /= 3
    else:
        i -= 27
        for _ in range(4):
            var_name += 'exc'[i%3]
            i /= 3
    var[c] = var_name

    # decompose code point into powers of two
    rem = c
    pows = []
    while rem:
        pows.append(rem&-rem)
        rem -= rem&-rem

    # define this variable
    front = 'exec"{}={}'.format(var[c], var[pows.pop()])
    back = '"'
    for i, p in enumerate(pows):
        front += '%'*(2**i) + 'c' + var[p]
        back += '%' + var[43]
    output += front + back + '\n'

# initialise the unpacker
output += 'exec"""{}=""\n"""\n'.format(var['prog'])
i = 0
length = len(unpacker)
while i < length:
    if (length-i) % 4 == 0:
        # concat 4 characters at a time
        w, x, y, z = [var[ord(unpacker[i+j])] for j in range(4)]
        output += 'exec"{}%c={}%%{}%%{}%%{}%%{}"%{}\n'.format(var['prog'], 
                    var['template'], w, x, y, z, var[43])
        i += 4
    else:
        output += 'exec"""{}%c="%%c"%%{}"""%{}\n'.format(var['prog'],
                    var[ord(unpacker[i])], var[43])
        i += 1

# encode source data
output += var['data'] + '="""'
output += '\n'.join(var[ord(c)] for c in source)
output += '"""\n'

# execute the program
output += 'exec"exec%c{}"%{}'.format(var['prog'], var[32])

print output

Спробуйте в Інтернеті


Ви можете додати декілька чеків, щоб побачити, чи програма введення вже обмежена необхідним набором символів, а якщо так, просто кот.
mbomb007

26

Математика, 5 4 символи

I[]

є символом Unicode приватного використання , який виконує функції оператора, Functionщо дозволяє писати літерали для неназваних функцій із названими аргументами. Характер схожий на Mathematica, тому я буду використовувати його для решти цієї відповіді для наочності.

З їх допомогою можна реалізувати S, Kі Iкомбінатори комбінаторної логіки:

I -> II↦II
K -> II↦III↦II
S -> II↦III↦IIII↦II[IIII][III[IIII]]

Одне з синтаксичних питань у тому, що це дуже низький пріоритет, який буде проблемою, якщо ми спробуємо передати аргументи цим комбінаторам. Як правило, це можна виправити, додавши комбінатор Cу круглі дужки (C), але у нас немає дужок. Однак ми можемо використовувати Iта []обробляти Cякусь іншу магію, яка має достатньо високий пріоритет, і ми можемо використовувати її згодом:

I[C][[I[[]]I]]

І, нарешті, написати заяву A x y z(де Aзнаходиться комбінатор « в дужках» , як показано вище, і x, y, zможе або не може бути укладена в дужках, або можуть бути великими виразами), ми можемо написати:

A[x][y][z]

Це залишає питання про те, як насправді працює еквівалент дужок. Я спробую це пояснити приблизно в тому порядку, як я його придумав.

Тому річ, яку ми маємо синтаксично згрупувати, - це дужки []. Дужки з'являються у Mathematica двома способами. Або як виклик функцій f[x], або як оператор індексації f[[i]](що насправді просто скорочення Part[f, i]). Зокрема, це означає, що ні ситокс, [C]ні симпатія не [[C]]є. Нам щось потрібно перед цим. Теоретично це може бути чим завгодно. Якщо ми використаємо те, Iщо вже є, ми можемо отримати, I[C]наприклад. Це залишається неоціненим, оскільки Iце не є одинарною функцією (це зовсім не функція).

Але зараз нам потрібен якийсь спосіб витягти Cзнову, тому що в іншому випадку це насправді не буде оцінено, коли ми намагатимемося передати йому аргумент x.

Ось тут це стане в нагоді, яке f[[i]]можна використовувати для довільних виразів f, а не лише списків. Якщо припустити, що fсама має форму head[val1,...,valn], то f[[0]]дає head, f[[1]]дає val1, f[[-1]]дає valnтощо. Тому нам потрібно отримати 1або -1витягнути Cзнову, тому що I[C][[1]]або I[C][[-1]]оцінює до C.

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

Але це залишає одну остаточну проблему: як ми насправді множимось Iсамі по собі? Ми не можемо просто писати, IIтому що це розбирається як єдиний символ. Нам потрібно відокремити ці лексеми, не a) змінивши їх значення і b) використовуючи будь-які нові символи.

Заключний біт магії - це фрагмент незадокументованої поведінки: f[[]](або рівнозначно Part[f]) є синтаксисом, що відповідає дійсності, і повертається fсам. Отже, замість того, щоб множити Iна I, ми множимо I[[]]на I. Вставлення дужок змушує Mathematica шукати новий маркер після цього і I[[]]Iоцінює так, -1як потрібно. І ось так ми закінчуємо I[C][[I[[]]I]].

Зауважте, що ми не могли використовувати I[]. Це виклик функції без аргументів I, але, як я вже говорив, Iце не функція, тому це залишатиметься неоціненим.


Чудова відповідь.
Патрік Стівенс

23

Python 2, 8 символів

exc'%~-0

Ці символи дозволяють перекласти / виконати будь-яку програму Python за допомогою рядків формату та exec. Хоча можливість перекладу будь-якої програми не потрібна для повноти Тьюрінга, це найменші символи, з яких я знаю, які так чи інакше роблять це TC. Це так потужно - це просто бонус.

Також може бути використаний подвійний лапки, а також будь-яка одна цифра, крім нуля. (Тепер, коли я думаю про це, 1безсумнівно , буде краще, в результаті чого в більш короткі програми, так як ви могли б використовувати 1, 11і 111, як добре.)

Ось програма print:

exec'%c%%c%%%%c%%%%%%%%c%%%%%%%%%%%%%%%%c'%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0%-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~0

Спробуйте в Інтернеті

Базова програма вимагає

exec''

Кожен символ, xдоданий до програми, вимагає (кількість рахунків):

  • % - 2**(n-1)
  • c - 1
  • - - ord(x)*2
  • ~ - ord(x)*2
  • 0 - 1

Виняток з цього полягає в тому, що для скорочення кодованої програми можна скоротити певні оптимізації / ярлики, наприклад використання %'0'символу0 замість 48 -~тощо.

Практичне використання (AKA golfing): я використовував цю тактику, щоб вирішити цю проблему не використовуючи додатковий характер гандикапу.

Кредит на список символів та програму кодування: тут

Для отримання інформації про пошук нижньої межі розміру програми (без оптимізації) дивіться цей коментар .

Кількість необхідних байтів збільшується O(2**n), тому цей метод не рекомендується використовувати для гольфу. Квінка з використанням цього обмеження джерела була б шалено довгою.


Якщо тільки пріоритет оператора виконується +або -перед %, ми можемо видалити символ.
mbomb007

Можливо, варто відзначити, що можливість перекладати кожну програму Python до скороченого набору символів не є необхідним для повноти Тьюрінга. Хоча я думаю, що буде важко отримати необхідну кількість контрольного потоку, не використовуючи execвсе одно.
Мартін Ендер

Це насправді навіть не є технічно перевершеною мовою, правда? Він має можливість викликати інтерпретатора для мови Turning Complete, яка є вбудованим інтерпретатором Python. Це може працювати будь-якою мовою, незалежно від того, перетворюється чи ні, якщо вона має можливість, наприклад, викликати команду оболонки до іншого інтерпретатора.
mmachenry

@mmachenry Python використовує власний компілятор та інтерпретатор. Це не використання іншої окремої мови. І в Python був створений епізодний перекладач, тож це Turing Complete. Використовуючи ці знання, ваш аргумент помилковий.
mbomb007

@ mbomb007 Жоден мій аргумент не хибний. Очевидно, Python - це повна мова. Розрахунок проводиться за допомогою виклику інтерпретатора Python з Python, використовуючи будь-який символ, який потрібно для внутрішнього виклику. Мова, якою ви вказуєте програму, - це просто кодування, а не мова програмування. Використовуючи це, банально зробити буквально кожну мову програмування Turing Complete, використовуючи символи 0 та 1 та переглядаючи вихідні файли як двійкові. Дух питання полягає в тому, щоб знайти синтаксичний підмножину фактичної мови.
mmachenry

23

C (незвітна), 24 18 13 символів

aimn[]={};,1+

Це охоплює всі програми форми

main[]={<sequence of constants>};

... де послідовність констант (форми 1 + 1 + 1 ...) містить машинне кодове представлення вашої програми. Це передбачає, що ваше середовище дозволяє виконувати всі сегменти пам'яті (мабуть, вірно для tcc [спасибі @Dennis!] Та деяких машин без біт NX). В іншому випадку для Linux та OSX вам, можливо, доведеться constдодати ключове слово, а для Windows, можливо, доведеться додати#pragma чітко позначений сегмент як виконуваний.

Наприклад, наступна програма, написана у вказаному вище стилі, друкує Hello, World!на Linux та OSX на x86 та x86_64.

main[]={111111111+111111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+11111+11111+11111+11111+11111+11111+11111+11111+111+11+11+11+11+11+11+1+1,1111111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+11111+11111+11111+11111+11111+11111+1111+1111+1111+111+111+111+111+111+111,1111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+111111+111111+111111+111111+11111+11111+11111+1111+1111+1111+1111+1111+1111+1111+1111+111+111+111+111+111+111+111+111+111+11+11+11+11+11+1+1+1+1+1+1+1,1111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+1111+1111+111+111+111+111+111+111+111+11+11+11+11+11+11+1+1+1+1,111111111+111111111+111111111+111111111+111111111+1111111+1111111+1111111+1111111+111111+111111+1111+1111+1111+1111+1111+1111+111+111+111+111+111+11+11+11+11+1+1+1+1,111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+1111+1111+1111+111+111+111+111+111+11+11+11+11+11+11+1+1+1+1+1+1,111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+111+111+111+111+111+111+11+11+11+11+11+11+11+1,1111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+1111+1111+1111+1111+1111+1+1+1+1+1,1111111111+111111111+111111111+111111111+111111111+111111111+111111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+11111+11111+11111+11111+1111+1111+111+111+111+111+111+111+111+111+111+11+11+11+11+11+1+1+1+1+1+1+1+1+1,1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+111111+111111+111111+111111+11111+11111+1111+1111+1111+1111+1111+1111+1111+111+111+111+111+111+11+11+1+1+1+1+1,1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+111111+11111+11111+11111+11111+1111+1111+1111+1111+1111+111+11+1+1+1+1+1,1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+11111+111+111+111+111+1+1+1+1+1+1+1,1111111111+1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+11111+11111+1111+1111+111+111+111+111+111+111+111+111+111+11+11+11+11+11+11+1+1+1,1111111111+111111111+111111111+111111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+111111+111111+1111+1111+1111+1111+1111+1111+1111+1111+111+111+111+111+111+111+111+111+111+1+1+1+1+1+1,111111+111111+11111+11111+11111+11111+11111+11111+11111+1111+1111+1111+1111+1111+1111+1111+1111+111+111+111+11+11+11+11+11+11+11+11+11+11,11111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+1111+1111+111+111+111+111+111+111+11+11+11+11+11+11+11+1+1+1,111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+1111+1111+111+111+111+11+11+11+1,111111111+111111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+11111+11111+1111+1111+111+11+11+1+1+1+1+1,11111+11111+11111+11111+1111+1111+1111+1111+111+111+111+111+111+111+111+111+111+11+11+11+1+1+1+1+1};

Спробуйте в Інтернеті!


2
@GB: Zero досить просто уникнути використання принаймні x86 машинного коду (це не дуже важлива інструкція), тим більше, що проблема трапляється лише у тому випадку, якщо у вас є чотири нульові байти підряд.

2
@GB На машині з 32-бітовими 0==1111111111+1111111111+1111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+111111111+11111111+11111111+11111111+11111111+11111111+11111111+1111111+1111111+1111111+1111111+1111111+111111+111111+111111+111111+11111+11111+11111+11111+11111+11111+11111+111+111+111+111+111+11+11+11+11+11+11+11+1
входами

3
tcc дозволяє вам піти без const. tio.run/nexus/…
Денніс

8
@GB Я щойно зрозумів, що коротше представлення 0 є1==11
roofcat

3
@ wizzwizz4, це ні в якому разі не чистий C, і ні в якому сенсі це не робить Тьюрінга завершеним. У ньому немає семантики С. Оскільки ви так чи інакше покладаєтесь на деталі середовища компілятора та виконання, щоб отримати взагалі що-небудь запущене, ви можете також дозволити ввести довільну точку входу.
Джон Боллінгер

20

Сітківка , 6 символів

$%`{¶

А також лінійні стрічки (0x0A).

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

На доказ. Я опишу простий спосіб перевести системи циклічних тегів на Retina за допомогою вищевказаних символів.

Спочатку ми будемо використовувати `і {для двійкового алфавіту замість 0і 1. Вони зручні, оскільки їх не потрібно уникати в регулярному вираженні, але вони мають значення як для сітківки, так і для синтаксису заміщення. Я використовую `для 0і {для 1, але цей вибір довільний. Крім того, ми збираємося повернути рядок (і постановки) в пам'ять, оскільки робота з останнім символом дозволяє нам використовувати, $а $`не ^і $', максимально використовувати символи.

Якщо початкове слово позначається Sі iназивається th (зворотна) продукція , отримана програма буде виглядати приблизно так:pi


S
{`

{$
¶p1$%``
``$

{$
¶p2$%``
``$

{$
¶p3$%``
``$

...

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

Давайте розглянемо, що робить програма:


S

Почнемо з ініціалізації робочого рядка до початкового слова.

{`

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

Кожне виробництво обробляється в два етапи. Спочатку ми маємо справу з тим, що провідним (або в нашому випадку кінцевим) символом є {, у цьому випадку ми використовуємо виробництво:

{$
¶pi$%``

Режекс відповідає лише тому, що рядок закінчується {. Якщо це так, ми замінюємо його на:

  • Лінійний податок ( ). Ми працюватимемо лише з останнім рядком робочої рядки, тому це ефективно відкидає робочу рядок до цього часу (саме тому використання пам'яті програми буде зростати та зростати).
  • Поточне виробництво (pi ), яке ми попередньо додаємо до робочого рядка (де система циклічних тегів додає його).
  • Попередній робочий рядок, що залишився ( $%`). Ось чому нам потрібно вставити : $%`підбирає все, що залишилося від матчу, але тільки в тому ж рядку. Отже, це не бачить усіх мотлоху, який ми залишили від попередніх постановок. Цей трюк дозволяє нам узгоджувати щось у кінці робочого рядка, щоб вставити щось на початку робочого рядка, не використовуючи щось на зразок (.+)і$1 що суттєво підірве кількість потрібних нам символів.
  • Один backtick ( `). Це ефективно замінює {( 1-символ), який ми відповідали, `( 0-символ), щоб наступний етап не знав, ми вже обробляли поточне виробництво чи ні.

Друга частина кожного виробництва - це тривіальний випадок, коли виробництво пропускається:

``$

Ми просто видаляємо трейлінг `. Причина, що нам потрібні два `на першому рядку, полягає в тому, що Retina вважає перший backtick дільником між конфігурацією та регулярним виразом. Це просто дає йому порожню конфігурацію, щоб ми могли використовувати зворотні посилання в самому регулярному виразі.


20

Java 7, 18 17 символів

\bcdefu0123456789

Весь вихідний код Java може бути зменшений до точок коду unicode. "a" не потрібен, оскільки використовується лише для*:jJzZ . Зірочка використовується для множення або блокування коментарів. Множення - це лише повторне додавання, і ви можете використовувати замість них однорядкові коментарі (або просто їх опустити). Двокрапка використовується для потрійних операторів, для яких ви можете використовувати оператор if, а також петлі foreach, які можна замінити нормальними для циклів. j і z не є частиною жодного ключового слова java.

Спроба видалити будь-який інший символ вимагає від нас додати принаймні один із символів, необхідних у плиті котла Java class a{public static void main(String[]a){}}. Дивіться нижче:

1 -> a (which has already been removed)
2 -> r (required for "String")
3 -> S (required for "String")
4 -> t (required for "static")
5 -> S (required for "String")
6 -> v (required for "void")
7 -> g (required for "String")
8 -> ( (required for "main(String[]a)"
9 -> i (required for "static")
b -> { (required for "class a{")
c -> l (required for "class")
d -> } (required for "(String[]a){}}")
e -> n (required for "main")
f -> o (required for "void")

Ось приклад програми Hello World Спробуйте це в Інтернеті!

Java 8, 16 символів

\bdefu0123456789

Дякуємо ais523, що вказав на це. Java 8 дозволяє інтерфейсам мати статичні методи, що означає, що ми можемо скинути "c", тому що нам це не потрібно для "l" у "class". "c" використовується для ,<lL\|того, що ми в кінцевому підсумку втрачаємо трохи більше функціональності Java, ніж коли ми видаляли "a", але у нас все ще достатньо, щоб бути завершеним. Спробуйте в Інтернеті!


3
Безумовно, зрозуміти, яку з шістнадцяткових цифр можна опустити - це цікава частина вирішення цього питання на Java? :)
Мартін Ендер

@MartinEnder абсолютно. Я планую працювати над цим більше, коли отримаю деякий час
Poke

6
І я, який був готовий щось написати Java, 127 characters... Гарний, Пок;)
Олів'є Григоар

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

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

19

Лабіринт , 5 символів

~{}

Плюс рядкові канали (0x0A) та пробіли (0x20).

Я збираюся накреслити доказ у вигляді скорочення від Smallfuck (зменшений варіант Brainfuck, який використовує 1-бітні комірки). Зауважте, що Smallfuck сам по собі не є повним Тюрінгом, оскільки мова вказує, що його стрічка повинна бути кінцевою, але ми збираємось припустити варіант Smallfuck з нескінченною стрічкою, який би потім був повним Тьюрінгом (оскільки Лабіринт не має пам'яті обмеження по дизайну).

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

Лабіринт має дві стеки, які спочатку заповнені нескінченною (неявною) кількістю 0s. {і }змістити верхнє значення між цими стеками. Якщо ми розглянемо верхню частину основного стека як поточну "клітинку", то ці дві стеки можна розглядати як дві напівскінченні половинки нескінченної стрічки, яку використовує Smallfuck. Однак буде зручніше мати дві копії кожного значення стрічки на стеках, щоб забезпечити інваріант, згаданий вище. Тому <і >буде переведений на {{і }}відповідно (ви можете поміняти їх , якщо хочете).

Замість того , щоб значення осередків 0і 1, ми використовуємо 0і -1, що ми можемо перемикатися між з ~(побітовое заперечення). Точні значення не мають значення для цілей Тюрінга. Ми повинні змінити обидві копії значення на стеку, що дає нам знову рівний переклад: *стає ~}~{.

Це залишає команди управління потоком []. Лабіринт не має явного керуючого потоку, але замість цього потік управління визначається компонуванням коду. Нам потрібні пробіли та канали ліній, щоб зробити це компонування.

По-перше, зауважте, що ~~це не-оп, оскільки вони ~фактично скасовують. Ми можемо використовувати це для довільно довгих шляхів у коді, якщо їх довжина є рівною. Тепер ми можемо використовувати наступну конструкцію для перекладу AA[BB]CCв Лабіринт (я використовую подвійні літери, щоб розмір кожного фрагмента в Лабіринті був рівним, як це гарантує інваріант):

      ~~~~
      ~  ~~~
AA~~..~~~~ ~CC
   ~     ~
   ~     ~
   ~     ~
   ~~~BB~~

Ось ..відповідна кількість, ~яка відповідає ширині BB.

Ще раз зауважте, що ширина конструкції залишається рівною.

Тепер ми можемо переглядати, як працює ця петля. Код вводиться через AA. Перший ~~нічого не робить і дозволяє нам дійти до перехрестя. Це приблизно відповідає [:

  • Якщо поточне значення комірки дорівнює нулю, IP продовжується прямо вперед, що в кінцевому рахунку пропустить BB. ..Частина досі немає-оп. Тоді ми досягаємо синглу ~на іншому стику. Зараз ми знаємо, що поточне значення не дорівнює нулю, тому IP повороту на північ. Він іде навколо вигину вгорі, поки не досягне іншого стику після шести ~. Тож у цей момент поточне значення все ще не дорівнює нулю, і IP знову повертає свою чергу, щоб рухатися на схід у напрямку до CC. Зауважте, що три ~перед CCповерненням поточного значення 0, як це має бути, коли цикл був пропущений.
  • Якщо поточне значення комірки на початку циклу не дорівнює нулю, IP бере поворот на південь. Він пробігає ще шість, ~перш ніж досягти BB(які нічого не роблять), а потім ще шість, ~перш ніж дійти до наступного вузла. Це приблизно відповідає ].
    • Якщо поточна комірка дорівнює нулю, IP продовжує рухатися на північ. Наступний ~робить значення не нульовим, так що IP приймає цей другий перехід, який об'єднує шлях із випадком, коли цикл був пропущений повністю. Знову ж таки, три ~повертають значення до нуля, перш ніж досягти CC.
    • Якщо поточна клітинка не дорівнює нулю, IP бере поворот на захід. Є ~перед наступним переходом, а це означає, що в цей момент значення струму дорівнює нулю, так що IP продовжує йти на захід. Тоді буде непарне число, ~перш ніж IP знову досягне початкового з'єднання, так що значення повертається -1і IP рухається на південь до наступної ітерації.

Якщо програма містить певні цикли, то найперше AAпотрібно поширити вгорі програми, щоб IP знайшов правильну комірку для початку:

~     ~~~~
~     ~  ~~~
AA~~..~~~~ ~CC
   ~     ~
   ~     ~
   ~     ~
   ~~~BB~~

Ось це. Зауважте, що програми, спричинені цим скороченням, ніколи не припиняться, але це не є частиною вимог Тюрінг-повноти (врахуйте правило 101 або Fractran).

Нарешті, нам залишається питання, чи оптимально це. Що стосується символів навантаження, я сумніваюся, що це можливо зробити краще, ніж три команди. Я міг бачити альтернативну конструкцію на основі машин Мінського з двома регістрами, але для цього знадобиться =()або =-~, маючи лише одну команду маніпулювання стеком, але потім дві арифметичні команди. Я був би радий, що я на цьому підтвердив неправильність. :)

Щодо команд компонування, я вважаю, що канали ліній необхідні, оскільки корисний потік управління неможливий в одному рядку. Однак пробіли технічно не потрібні. Теоретично, можливо, можна створити конструкцію, яка заповнює всю сітку ~{}(або =()або =-~), або використовує нерівний макет, де лінії не однакової довжини. Однак написати подібний код неймовірно складно, тому що Лабіринт тоді буде ставитись до кожної комірки як до з'єднання, і вам потрібно бути дуже обережним, щоб код не розгалужувався, коли цього не хочете. Якщо хтось може довести чи спростувати, чи можливо пропустити простір для Тьюрінга-повноти, я був би радий дати значну нагороду за це. :)


19

Haskell, 5 7 символів

()\->=;

Як функціональна мова, Haskell, звичайно, має лямбда, тому імітувати обчислення лямбда легко. Синтаксис лямбда є(\variable->body)argument такий, що нам потрібні принаймні символи ()\->.
Крім того, нам потрібна необмежена кількість змінних символів, щоб ми могли будувати довільні лямбда-вирази. На щастя , нам не потрібні нові символи для цього, так як (>), (>>), (>>>), ..., всі дійсні імена змінних. Насправді кожна комбінація \->внутрішніх дужок є дійсним ім'ям змінної, за винятком справедливих \і ->, які зарезервовані для лямбда-виразів, і --яка починає коментар до рядка.

Приклади:

  • S = (\(>)(\\)(-)->(>)(-)((\\)(-)))види до(t2 -> t -> t1) -> (t2 -> t) -> t2 -> t1
  • К = (\(>)(-)->(>))види доt -> t1 -> t
  • I = (\(>)->(>))види доt -> t

Редагувати: Однак, як в коментарях вказувало ais523 , ця конструкція реалізується введене обчислення лямбда , яке само по собі не є повним Тюрінгом, оскільки йому не вистачає можливості вводити нескінченні петлі. Щоб виправити це, нам потрібна деяка функція, яка виконує рекурсію. Поки ми використовували безіменні лямбда, які не можуть назвати себе, тому що, ну, вони не мають імені. Отже, ми повинні додати символи =та ;реалізувати fixфункцію:

(>)=(\(-)->(-)((>)(-)));   -- equivalent to: f =(\ x -> x ( f  x ));

З цією декларацією наше лямбда-обчислення стає Тьюрінг завершеним, однак додавши, =і ;нам більше не потрібні лямбда, як видно з відповіді Німі, яка використовує просто()=; .


Чи технічно це не буде видалено під час компіляції main?
PyRulez

4
Просто набране числення комбінатора SKI не є повним Тьюрінгом; для цього вам потрібне нетипічне обчислення лямбда. На жаль, як згадують ваші демонстрації, Haskell за замовчуванням ставить набрану інтерпретацію коду.

@PyRulez Відповідно до правил за замовчуванням, я вважав, що функції прийнятні.
Лайконі

@ ais523 Комбінатори SKI - лише приклад, використовуючи задані позначення, можна побудувати довільні лямбда-терміни, наприклад церковні цифри та функції на них.
Лайконі

@ ais523, скільки комбінаторів потрібно набрати начинку лямбда? Я думаю, що вам просто потрібен комбінатор y, правда?
PyRulez

18

CJam, 3 символи

Видалено )за пропозицією Мартіна Ендера

'(~

Схожий на Python, наведений як приклад.

Використовуючи '~ви можете отримати ~символу. Потім, використовуючи (, ви можете декрементувати його, щоб отримати будь-який символ, який ви хочете ( ~це останній символ для друку ASCII). ~оцінює будь-який рядок як звичайний код CJam. Рядки можуть бути побудовані шляхом отримання символу [(шляхом декрементування ~), вирівнювання його, введення деякої послідовності інших символів, а потім вирівнювання символу ]. Завдяки цьому ви можете створювати та виконувати будь-яку програму CJam, використовуючи лише ці три символи.

Обчислення лише 2 + 2 '(~


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

1
Мені вдалося значно '~((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((~'~(((((((((((((((((((((((((((((((~'~(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((~
пограти

@Zwei чудово, що відповідає вашому імені
Chromium

18

Мозг-Флак , 6 символів

(){}[]

Brain-Flak - це мінімалістична мова з лише 8 доступними символами. Однак можна довести, що існує підмножина Brain-Flak, яка також є Тьюрінг повною, використовуючи лише 6 символів.

Перше, що ми зробимо - це реалізувати Minsky Machine з лише однією стечкою Brain-Flak. Якщо ми можемо довести, що машина Менського можлива лише з одним стеком, ми можемо показати, що Brain-Flak є Turing завершеним без<> і []ніладів. Це не врятує будь-яких персонажів негайно, але в майбутньому, коли ми покажемо, що <...>це не потрібно.

Машина Мінського - це тип повного автомата Тьюрінга, який має обмежену кількість необмежених регістрів і дві інструкції:

  • Збільшення реєстру

  • Якщо ненульовий декремент в іншому випадку переходить до вказаної інструкції

Щоб налаштувати структуру goto в Brain-Flak, ми можемо використовувати наступний фрагмент:

(({}[()])[(())]()){(([({}{})]{}))}{}{(([({}{}(%s))]{}))}{}

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

Ми використовуємо це як обгортку для всіх наших інструкцій на машині Minsky.

Збільшити конкретний регістр досить легко, не перемикаючи стек. Це можна досягти за допомогою такої строкової формули:

"({}<"*n+"({}())"+">)"*n

Наприклад, щоб збільшити 3-й регістр, ми напишемо наступний код:

({}<({}<({}<({}())>)>)>)

Тепер нам залишається здійснити другу операцію. Перевірка, чи число дорівнює нулю, є досить простим у Brain-Flak:

(({})){(<{}%s>)}{}

буде виконуватися лише в тому %sвипадку, якщо TOS дорівнює нулю. Таким чином, ми можемо зробити свою другу операцію.

Оскільки Мінські машини є Тьюрінгом повний Мозок-Флак також є Тюрінг повним без використання <>і[] операцій.

Однак ми не зменшили кількість символів ще тому , що <...>і [...]досі використовуються. Це можна виправити простою заміною. Оскільки <...>насправді рівнозначний [(...)]{}у всіх випадках. Таким чином, Brain-Flak є Turing завершеним без використання символів <та >символів (плюс усі не-опери).


"тому що <...>і [...]досі використовується." Однак ви не видалили [...]. Виправте, будь ласка.
CalculatorFeline

Питання: Чи [...]справді це потрібно? Натискання 0 можна здійснити з початку ({})(але воно покладається на порожній стек, тому 0 повинні бути ретельно переміщені). Основна проблема полягає в тому, щоб знизити стек без доступу до цього <...>(що вже неможливо імітувати)
КалькуляторFeline

16

> <> , 3 символи

> <> можна 1p-виконати в 3 з , які роблять:

1          Push 1
p          Pop y, x, c and put the char c in cell (x, y) of the codebox
-          Subtraction: pop y, x and push x-y

pзабезпечує відображення, змінюючи 2D вихідний код, розміщуючи символи у кодовій коді. З 1-, ви можете натиснути будь-яке число на стек, оскільки 1-віднімає одне і 111-1--( x-(1-1-1) = x+1) додає одне.

Після того, як всі 1p-команди виконані, вказівник інструкції загортається, що дозволяє йому виконувати "реальний" код.

Приклад програми, яка обчислює числа Фібоначчі (з цієї відповіді ):

111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--11-11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1--11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1--11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1--11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1--11-p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--11-1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--11p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1-1-1-1--1p111-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1--111-1-1-1-1-1-1-1-1-1-1-1-1-1-1--1p

Спробуйте в Інтернеті! Після того, як всі 1p-команди виконані, кодове поле виглядає приблизно так:

01aa+v1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1- ...
@+&1->:?!;&:nao:

Якщо заборонено все після vпершого рядка, це стандартна програма <>> Фібоначчі.


13

баш, 9 символів

01456\$ '

Bash має синтаксис $'\nnn'для введення символів з їх вісімковими значеннями ascii. Ми можемо ввести evalкоманду в цьому форматі як $'\145\166\141\154'. Спочатку перетворюємо бажаний результат у його восьмеричні значення. Потім ми перетворюємо будь-які вісімкові значення, використовуючи цифри, відмінні від 0, 1, 4, 5 і 6, у вирази, що оцінюються за вказаними вісімковими значеннями, використовуючи $(())та віднімаючи, додаючи evalпередню частину. На останньому кроці ми додаємо ще один evalі перетворюємо круглі дужки та знак мінус у їх вісімкові значення. Використовуючи цей метод, ми можемо виконати будь-яку команду bash, тому ця підмножина закінчується.

Приклад:

dc стає

$'\144\143' що стає

$'\145\166\141\154' \$\'\\144\\$((144-1))\' що стає

$'\145\166\141\154' $'\145\166\141\154' $'\$\\\'\\\\144\\\\$\050\050144\0551\051\051\\\''


12

Інцидент , 2 символи

Не має значення, які два символи ви обираєте; будь-яке поєднання двох октетів є повним Тьюрінгом в Інциденті.

Насправді довести це набагато складніше, ніж можна було очікувати, і під час написання сторінки дискусія на Esolang про інцидент присвячена цій проблемі. Я спробую дати короткий виклад найпростішого відомого доказу нижче.

Перед доказом деяке тло. Інцидент підводить маркери, які використовуються в програмі, дивлячись на джерело (маркер - це рядок, який рівно тричі з’являється у джерелі, не є підрядком іншого маркера і не перетинається з іншим потенційним маркером). Будучи такою, будь-яка програма може бути перетворена для використання в значній мірі будь-якого набору символів, змінивши те, що є маркерами; мова є повною Тьюрінгом (і вона також є повнотою для вводу / виводу!), незважаючи на те, що програмувати неймовірно складно, тому "все", що вам потрібно, - це метод кодування лексем, щоб вони працювали лише з двома символами.

А тепер ось короткий виклад доказів (який знайшов Шріян, математик-резидент Есоланг). Ідея полягає в тому, що ми кодуємо маркер, використовуючи дві копії одного символу (скажімо 1) у великому морі іншого символу (скажімо0 ). Відстань між 1s відрізняється для кожного маркера, але завжди кратне 4. Тоді для прокладки між маркерами ми використовуємо додатковий список 0s з a 1в середині, але число 0s з кожної сторони 1є не кратне 4, а скоріше число, унікальне для конкретної частоти програми, яка не з’являється в іншому місці програми. Це означає, що кожен11всередині прокладки може з’являтися лише два рази, тому не буде частиною маркера; кожен призначений маркер містить рівно два 1s, і жоден фіктивний маркер не може містити більше одного 1. Потім ми просто додаємо трохи накладки на бік, щоб видалити всі можливі маркери, що містять один 1або нуль 1s, додавши принаймні чотири копії.


11

Сітківка , 3 символи

{`

і новий рядок.

По-перше, нам потрібен новий рядок, щоб можна було робити заміни (необхідні, якщо ми не хочемо вмістити всю програму в один регулярний вираз, для чого знадобиться більше символів); і `і {є найменш символьного інтенсивним способом зробити петлю. Виявляється, нам більше нічого не потрібно.

Наша цільова мова для реалізації - це детермінований варіант Thue (недетермінізм не потрібен для Тьюрінга-повноти; можна написати програму Thue, щоб правильно працювати незалежно від того, який порядок оцінки використовується). Основна ідея - скластиpattern::=replacement в

`pattern
replacement

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

Звичайно, це означає, що нам потрібно довести Thue Turing-завершеним справедливим {та `в моделях та заміні, але це досить просто; замінити символ з кодом ASCII п з `, п + 1 {, а інший `. Очевидно неможливо, щоб шаблон узгоджувався де-небудь, крім меж символів, тому це в кінцевому підсумку буде робити те саме, що і в оригінальній програмі.


1
"Програми вт продовжують працювати до тих пір, поки не можливі більше заміни, і це змушує сітківку працювати однаково", за єдиним винятком, що Retina припиняється достроково, якщо один прохід по всій програмі не змінить рядок. Таким чином, ви навіть отримуєте просте виявлення нескінченного циклу безкоштовно.
Мартін Ендер

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

10

Брахілог , 5 символів

~×₁↰|

Цей підмножина символів дозволяє нам реалізувати версію Fractran, в якій єдиними числами, які можуть з’являтися, є продукти повторних з'єднань (тобто продукти чисел, які можна записати у десятковій формі, використовуючи лише цифру 1). (з цілим числом як індекс) ділить поточне значення на це ціле число, але лише в тому випадку, якщо воно ділиться точно (інакше воно «не вдається» і шукає інший випадок запуску; |розділяє випадки). ×дозволяє нам помножити на ціле число. Тож за допомогою ~×₁|ми можемо реалізувати один крок виконання Fractran. Тоді ми дозволяємо повторити, запустивши всю програму знову за новим поточним значенням. Ось приклад дуже простої програми Fractran ( 11111111111111111111111/111), перекладеної на Брахілог.

Так це Тюрінг завершений? Все, що нам потрібно зробити Fractran Turing завершеним, - це достатньо велика кількість простих чисел (достатньо, щоб написати перекладача для повної мови Тюрінга на самому Fractran). Існує п'ять перевірених і чотири підозрюванихповторно з'єднати праймери, крім, цілком можливо, тих, які ще не були виявлені. Це насправді більше, ніж нам потрібно в цьому випадку. Програма перевіряє можливості зліва направо, тому ми можемо використовувати один прайм в якості вказівника на вказівки, а ще два - як лічильники, демонструючи повноту Тюрінга лише трьома праймерами (це теж добре, тому що це дозволяє використовувати повторні з'єднання з 2, 19 , і 23 цифри, без необхідності вдаватися до перевірених, але дратуючих-великих з'єднань з 317 або 1031 цифрами, що зробить вихідний код досить важким для запису). Це дозволяє реалізувати машину Мінського з двома лічильниками (достатньо для Тюрінга-повноти).

Ось як конкретно працює компіляція. Ми будемо використовувати наступні дві команди для нашої реалізації машини Minsky (це відомо, що Тьюрінг завершений), і кожна команда матиме ціле число як мітку:

  • Мітка L: Якщо лічильник {A або B} дорівнює нулю, перейдіть до X. Інакше зменшіть його та перейдіть до Y.
  • Мітка L: Лічильник збільшення {A або B}, потім перейдіть до Z.

Ми вибираємо, яку команду виконувати, розміщуючи повноваження 11 у знаменнику, першими вищими силами; показник 11 - мітка команди. Таким чином, перша фракція, яка відповідає, буде командою, що виконує в даний момент (тому що попередні не можуть ділитися на всі 11). У разі команди декременту ми також розміщуємо в знаменнику коефіцієнт 1111111111111111111 або 111111111111111111111, для лічильника A або B відповідно, і слідуємо за ним з іншою командою без цього фактора; випадок "декремент" буде реалізований за першою командою, "нульовий" випадок - другою. Тим часом "goto" буде оброблятися відповідною потужністю 11 в чисельнику, а "прирощення" через коефіцієнт 1111111111111111111 або 11111111111111111111111 в чисельнику.


Якась конкретна причина, що ви не можете використовувати парні поєднання копрімерів?
CalculatorFeline

@CalculatorFeline: Ні, але я не думав про них, поки не знайшов конструкції, яка їм не потрібна. Це, безумовно, допоможе в програмах для гольфу, написаних з цим набором символів.

Крім того, всі повторні з'єднання> 1 є парними копіймами (подумайте про це)
CalculatorFeline

@CalculatorFeline: Ні, вони не є. 111 і 111111 обидва поділяються на 3, досить очевидно.

* жодне повторне з'єднання не ділить ще одне з'єднання
CalculatorFeline

10

Befunge-98, 3 символи

Наскільки я знаю, Befunge-98 повинен бути завершеним, тому нам просто потрібно показати, як можна створити будь-яку програму Befunge-98, використовуючи лише три символи. Моє початкове рішення спиралося на наступні чотири символи:

01+p

Ми можемо отримати будь-яке додатне ціле число на стек, додавши 1разом з +командою кілька значень , а для нуля ми просто використовуємо 0. Після того, як ми зможемо натиснути будь-яке число, яке ми хочемо, ми можемо скористатися цимp (put), щоб записати будь-яке значення ASCII в будь-яке місце на полі Befunge.

Однак, як вказував Sp3000 , насправді можна обійтись лише трьома символами:

1-p

Будь-яке від’ємне число можна обчислити, починаючи з, 1а потім повторно віднімаючи 1(наприклад, -3 було б 11-1-1-1-). Тоді будь-яке додатне число можна представити відніманням 1-n від 1, де 1-n - від'ємне число, з яким ми вже знаємо, як поводитися (наприклад, 4 = 1 - (- 3), що було б111-1-1-1-- ).

Таким чином, ми можемо використовувати наші три символи для написання свого роду завантажувача, який повільно генерує фактичний код, який ми хочемо виконати. Після того, як цей завантажувач буде завершений, він завернеться до початку першого рядка ігрового поля, який у цей момент повинен містити початок новоствореного коду.

Як приклад, ось завантажувач, який генерує код Befunge, необхідний для підсумовування 2 + 2 та виведення результату: 22+.@

І для трохи складнішого прикладу, це "Hello World": "!dlroW olleH"bk,@


Це поліглот, ті ж символи можна використовувати для <<> та його похідних. Хороша робота!
Сік

2
Befunge-98 є здійсненним в 3 з , 1p-а також
Sp3000

@ Sp3000 Звичайно, так! Я був впевнений, що, мабуть, був спосіб звести його до 3 символів. Дякую.
Джеймс Холдернес

9

Рубін, 8 ч

eval"<1+

Натхненний відповідями Python

Як це працює

  • eval може бути використаний для виконання довільної рядки.
  • "<1+ - мінімальний набір символів, необхідний для створення будь-якого рядка

Рядок в рубіні може бути побудований, використовуючи порожню рядок як початкову точку та додаючи до неї символи ascii, наприклад:

eval ""<<111+1<<11+11+11+1<<111<<11+11+11+1

насправді еквівалентний

eval ""<<112<<34<<111<<34

яка оцінює рядок

p"o"

8

OCaml, 9 символів

fun->()`X

Ці символи є достатніми для реалізації обчислення комбінатора SKI в OCaml. Помітно, що ми можемо уникнути використання простору з достатньою дужкою. На жаль, лямбда-вирази в OCaml вимагають цьогоfun ключового слова, тому більш коротке рішення неможливо. Ті ж букви можуть бути використані для побудови довільних імен змінних, якщо бажані більш складні лямбда-вирази.

S Комбінатор:

fun(f)(u)(n)->f(n)(u(n)) з типом ('a -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'c

K Комбінатор:

fun(f)(u)->u з типом 'a -> 'b -> 'b

Я комбінатор:

fun(f)->f з типом 'a -> 'a

Як зазначає ais523, недостатньо просто кодувати SKI. Ось кодування для Z з використанням поліморфних варіантів для маніпулювання системою типів. З цим моє підмножина має бути завершеним.

Z комбінатор:

fun(f)->(fun(`X(x))->(x)(`X(x)))(`X(fun(`X(x))y->f(x(`X(x)))y))

з типом (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b


2
Просто набране числення комбінатора SKI не є повним Тьюрінгом; для цього вам потрібне нетипічне обчислення лямбда. На жаль, як згадують ваші демонстрації, OCaml за замовчуванням ставить набрану інтерпретацію коду.

1
Тоді мені просто потрібно ще кілька символів, щоб дозволити використання поліморфних варіантів, які дозволять кодувати y-комбінатор (і аналогічно z-комбінатор).
Девін Лемахер

Що таке комбінатор Z?
CalculatorFeline

@CalculatorFeline Це суворий варіант y-комбінатора. Це необхідно в OCaml, тому що OCaml не лінивий. Ось посилання на сторінку wikipedia: en.wikipedia.org/wiki/…
Девін Лемахер

8

Сполучні мови на основі стека, 4 символи

Недовантажений

():^

GolfScript

{}.~

CJam

{}_~

GS2

  • backspace, tab @,, space (я знав, що GS2 багато використовував недруковані версії, але це смішно ...)

DC (запропоновано @seshoumara)

[]dx

Недостатнє завантаження було підтверджено Тюрінгом-повним лише з використанням ():^(завдяки резиденту математика Есоланга Шріджану). Доказ занадто довгий для пояснення тут, але якщо вам цікаво, ви можете прочитати про це тут .

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


Я розумію, що ви можете виконувати операції зі стеком з ними, але вам не потрібно принаймні числа, щоб заповнити цей стек, щоб зробити математичні обчислення? Або це зроблено в одинаковому режимі, використовуючи одну з 4 символів?
seshoumara

1
@seshoumara: Числа (і майже всі інші сховища даних) реалізуються дуже опосередковано при використанні цього методу. Існує щось на зразок двох-трьох, може, навіть чотирьох, рівнів абстракції, перш ніж потрапити на щось розпізнаване як арифметичне. Така річ поширена в доказах повноти Тьюрінга для дуже обмежених систем, як ця.

Я думав надсилати відповідь у dc, також на основі мови стека, але використовуючи інший метод, що включає більше знаків, ніж 4. dc не має оператора конкатенації, але у нього є еквівалентні ті, які ви згадуєте: [] d x. Чи може DC вписатись у ваш список?
seshoumara

@seshoumara: Так, здається, він має всі необхідні функції. Я додав його і зарахував вас.

Можливо, ви можете шукати FlogScript
mbomb007


7

Ракетка (схема), 4 символи

(λ)

Використовуючи лише λ, круглі дужки та простір, ми можемо безпосередньо програмувати в підмножині обчислення лямбди. Ми використовуємо символ λ для всіх ідентифікаторів, об'єднуючи їх разом, щоб надати довільно велику кількість унікальних ідентифікаторів.

Як приклад, ось класичний Омега-комбінатор, який замикається назавжди.

((λ (λλ) (λλ λλ)) (λ (λλ) (λλ λλ)))

6

Python 3, 9 символів

exc('%1+)

Дивіться мою відповідь Python 2 для основного пояснення. Ця відповідь ґрунтується на цьому.

Замість того, щоб просто використовувати ті самі символи, що й Python два, з додаванням (), ми можемо скинути символ, оскільки тепер ми використовуємо круглі дужки. Програми все ще матимуть основну форму

exec('%c'%stuff)

але ми скорочуємо довжину програми, використовуючи +замість -, а потім ми можемо видалити ~за допомогою1 замість 0. Потім ми можемо додати 1, 11та 111отримати необхідні значення ASCII.

Програма print() стає найкоротшою наступною:

exec('%c%%c%%%%c%%%%%%%%c%%%%%%%%%%%%%%%%c%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%c%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%c'%(111+1)%(111+1+1+1)%(11+11+11+11+11+11+11+11+11+1+1+1+1+1+1)%(11+11+11+11+11+11+11+11+11+11)%(111+1+1+1+1+1)%'('%')')

Спробуйте в Інтернеті

Ви можете думати собі, як можна створити байт NUL 0? Не бійся, молодий коник! адже ми маємо можливість використовувати і %для математики, створюючи нуль за допомогою 1%1.


Чому б вам хотілося байта NUL у своїй програмі?
NieDzejkob

@NieDzejkob На цьому сайті відповідь "чому" завжди є "тому що ми можемо". У цьому випадку, однак, це не буде повною реалізацією Python, якби ви не могли цього зробити, навіть якщо це просто видає помилку.
mbomb007

Вам не знадобиться байт NUL для повноти Тьюрінга; перекладач BF можна написати без одного
MilkyWay90

@ MilkyWay90 Щоправда, але чому б не рахувати це, якщо зможете?
mbomb007

6

PHP 7, 6 символів

'().;^

Ідея полягає в тому, що можна виконати довільний код за допомогою наступної конструкції:

('create_function')('','<code>')();

eval тут не буде працювати, оскільки це мовна конструкція і її неможливо викликати за допомогою змінних функцій.

create_function і код може бути записаний у вигляді об'єднання бітових XOR наявних символів:

(<char1_1>^<char1_2>^...).(<char2_1>^<char2_2>^...)...

Використовуючи ().;^для <charX_Y>, ми можемо отримати

()./:;<=JKLMXY^_bcdepqvw

та деякі недруковані символи. Це недостатньо, але тепер ми можемо також зателефонувати 'eXp'()та отримати численні символи:

''.'eXp'('eXp'('')) -> 1
''.'eXp'('eXp'('eXp'(''))) -> 2.718281828459
''.'eXp'('eXp'('eXp'('eXp'('eXp'(''))))) -> 3814279.1047602

Це дає нам 1, 2і 3(інші символи будуть проігноровані XOR, якщо інша рядок довжиною один символ). Відтепер ().;^123ми можемо генерувати всі набори ASCII.

Спробуйте в Інтернеті


5

Пайк, 5 символів

0h.CE

Це здатне створити нескінченно велике число, перетворити його в рядок і потім оцінити його як код Pyke.

Пояснення коду:

0- Додайте 0 до стеку. Це потрібно для запуску числа

h- Збільшення числа раніше. Повторивши це довільну кількість разів, ви можете створити числа, які нескінченно великі. Pyke підтримує bignums, як написано в Python, який використовує їх як за замовчуванням.

.C- Перетворіть число в рядок за допомогою наступного алгоритму: ( посилання Github )

def to_string(num):
    string = ""
    while num > 256:
        num, new = divmod(num, 256)
        string = chr(new) + string
    string = chr(num) + string
    return string

До цього моменту ми можемо створити довільну кількість рядків і натуральних чисел у Pyke з довільними значеннями. Числа можна створити у формі, що відповідає регексу, 0(h)*і рядки можна створити за допомогою 0(h)*.C. Їх можна переплести між собою, щоб створити довільну суміш рядків і цілих чисел.

E- оцінити рядок як код Pyke. Для цього використовується те саме середовище, що і Pyke-код, який вже працює, тому вони поділять такі речі, як вхідні дані.

Спроба доказу, що Пайк Тьюрінг завершений.

Один з найпростіших способів показу мови, який завершується, - це впровадити в нього Brainf * ck. У Pyke це, мабуть, набагато складніше, ніж у багатьох інших мовах, тому що операції зі списком та словниками майже не існують через відсутність необхідності в області, в якій Pyke призначений для запуску: .

По-перше, ми створюємо інтерпретатор для brainf * ck і кодуємо його за допомогою нашого алгоритму для створення числа, а потім виражаємо це число за допомогою 0і і h. Потім ми створюємо рядок, що містить код, який буде запускатися точно так само. Якби ми залишили це при цьому, у нас був би стос як

string containing brainf*ck code
string containing brainf*ck interpreter

Це означає, що код повинен бути у протилежному вигляді, оскільки стек Pyke перший в останньому.

Тепер для задоволення: інтерпретатор brainf * ck із колосальними 216 байтами!

Q~B"><ht.,".:=B;Z]1=L;W~Bo@D=c"ht"{I~c~LZ@EZ]1~LR3:=L)~c\,qIz.oZ]1~LR3:=L)~c\.qI~LZ@.CpK)~c"<>"{I~c"<>""th".:ZE=ZZ1_qI0=Z~L0"":0]10:=L)Z~LlqI~L~Ll"":1_]10:=L))~c\[qI~LZ@0qI\]~B~o>@~o+h=o))~c\]qI~o\[~B~o<_@-t=o)~o~BlN

Спробуйте тут!

Якщо ви хочете спробувати код у напівзавершеному, але редагованому вигляді, спробуйте його тут!

Для перетворення з рядка в число ви можете використовувати наступний код Python:

def conv(string, t=0):
    t *= 256
    t += ord(string[0])
    if len(string) != 1:
        return conv(string[1:], t)
    return t

(Майже) остаточне рішення можна спробувати тут!

Пояснення перекладача Brainf * ck

Спочатку давайте розділити програму на частини:

  • Ініціалізація:

Сігналы абмеркавання

Q~B"><ht.,".:=B;Z]1=L; - The initialisation part
Q~B"><ht.,".:          - input.replace("><+-.,[]", "><ht.,")
                       - replace the characters in brainf*ck with some modified ones. 
                       - this means we can `eval` the add and subtract bits easily.
             =B;       - set `B` to this.
                       - The `B` variable contains the instructions
                Z]1=L; - set `L` to [0]
                       - `L` contains the stack, initialised with 0
  • Основний цикл:

Сігналы абмеркавання

W~Bo@D=c !code! ~o~BlN - The main loop
W                      - do
 ~Bo@D=c               -  c=B[o++]
                       -  the c variable is used to store the current character.
                ~o~BlN - while
                ~o     -   o 
                     N -  ^ != V 
                  ~Bl  -   len(B)
                       -  this stops the program running once it's finished.
  • Інструкція
    • Збільшення / зменшення:+-

Сігналы абмеркавання

"ht"{I~c~LZ@EZ]1~LR3:=L) - The bit that does incrementing and decrementing
"ht"{I                 ) - if c in "ht"
        ~LZ@             -  L[Z]
                         -  `Z` contains the current stack pointer
      ~c    E            -  eval current character with ^ as an argument
                         -  returns the contents of `Z` either incremented or decremented
             Z]1~LR3:=L  - L[Z] = ^
  • Вхід ,:

Сігналы абмеркавання

~c\,qIz.oZ]1~LR3:=L) - The code for output 
~c\,qI             ) -  if character == ",":
      z.o            -    ord(input)
         Z]1~LR3:=L  -   L[Z] = ^
  • Вихід .:

Сігналы абмеркавання

~c\.qI~LZ@.CpK) - The code for input 
~c\.qI        ) - if c == ".":
      ~LZ@      -    L[Z]
          .C    -   chr(^)
            pK  -  print(^)
  • Зсув вліво / вправо <>::

Сігналы абмеркавання

~c"<>"{I~c"<>""th".:ZE=Z - main part 
~c"<>"{I                 - if "<>" in c:
        ~c"<>""th".:     -  c.replace("<>", "th")
                    ZE=Z -  Z = eval(char, Z)

Z1_qI0=Z~L0"":0]10:=L) - lower bound check
Z1_qI                ) - if Z == -1:
     0=Z               -  Z = 0
        ~L0"":         -  L.insert("", 0)
              0]10:=L  -  L[0] = 0

Z~LlqI~L~Ll"":1_]10:=L) - upper bound check
Z~LlqI                ) - if Z == len(L):
        ~Ll"":          -  L.insert("", len(L))
      ~L      1_]10:=L  -  L[-1] = 0
  • Умови [::

Сігналы абмеркавання

~c\[qI~LZ@0qI\]~B~o>@~o+h=o)) - Code for `[`
~c\[qI                      ) - if c == "[":
      ~LZ@0qI              )  -  if L[Z] == 0:
               ~B~o>          -     B[o:]
             \]     @         -    ^.find("]")
                     ~o+h=o   -   o = o + ^ + 1

- І ]:

Сігналы абмеркавання

~c\]qI~o\[~B~o<_@-t=o) - Code for `]`
~c\]qI               ) - if c == "]":
          ~B~o<_       -    reversed(B[:o])
        \[      @      -   ^.find("[")
      ~o         -t=o  -  o = o - ^ -1

5

Укладається, 5 символів

{!n:}

Це напрочуд коротко. Якщо Stacked може реалізувати кожну з комбінацій SKI, то це Turing Complete. Резюме:

  • I комбінатор - функція ідентичності. x -> x
  • K комбінатор - постійна функція. x -> y -> x
  • S комбінатор - функція заміщення. (x, y, z) -> x(z)(y(z))

Я комбінатор: {!n}

Тепер для складеної специфіки. {! ... }є n-лямбда. Це одинарна функція, аргумент якої неявно n. Потім з функції повертається останній вираз. Таким чином, {!n}це функція, яка бере аргумент nі приносить результат n.

K комбінатор: {!{:n}}

Тепер, {:...}це функція, яка не бере аргументів, і повертається .... Поєднуючи це з нашою формацією n-лямбда, ми отримуємо (додаючи пробіли для ясності):

{! { : n } }
{!         }   n-lambda. arguments: (n)
   { : n }     lambda.   arguments: ()
       n       yields n.

S Комбінатор: {n!nn!nnn:nnn{!n}!nn!nnn{!n}!n!!}

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

{n nn nnn:nnn{!n}!nn!nnn{!n}!n!!}

Це лямбда , яка приймає три аргументи, n, nnі nnn. Давайте замінити їх x, yі zдля ясності:

{x y z:z{!n}!y!z{!n}!x!!}

Два - {!n}!це лише функція ідентичності, щоб знову уникнути пробілів, де !означає "виконати". Отже, знову зменшення:

{x y z:z y!z x!!}

З поясненням:

{x y z:z y!z x!!}
{x y z:         }  three arguments
       z y!        apply `y` to `z` -- `y(z)`
           z x!    apply `x` to `z` -- `x(z)`
               !   apply `x(z)` to `y(z)` -- `x(z)(y(z))`

І тому це комбінатор S.


{n nn nnn:nnn{!n}!nn!nnn{!n}!n!!}містить пробіли.
CalculatorFeline

@CalculatorFeline Ви читали речення до цього? Гаразд, це виглядає трохи складніше. Отже, лямбда приймає аргументи, розділені неідентифікаційними символами. Таким чином, лямбда в заголовку еквівалентна:
Conor O'Brien

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