Вихід із прістінського світу


16

Це завдання базуються на ХЕЛКОМ Homba питання «s Програмування незайманого світу . З цього питання визначення первозданної програми:

Давайте визначимо незайману програму як програму, яка не має жодних помилок, але буде помилкою, якщо змінити її, видаливши будь-яку суміжну підрядку з N символів, де 1 <= N < program length.

Наприклад, три символьна програма Python 2

`8`

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

8`
``
`8

а також усі програми в результаті видалення підрядків довжиною 2 викликають помилки:

`
`

Якщо, наприклад, `8була програма, що не помиляється, тоді `8`вона не була б незайманою, оскільки всі результати видалення підрядків повинні помилятися .

Примітки:

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

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

Виграє найкоротша відповідь у байтах за кожну мову.


Я припускаю, що мови, які не помиляються, не можуть конкурувати?
Атако

@ATaco На жаль, так. Інші мови, такі як lisp, мають синтаксис, побудований таким чином, що зробити корисну незайману програму неможливо.
Шельваку

RIP Насправді / серйозно
ATaco

"Найкоротша відповідь у байтах для кожної мови виграє." Я не впевнений, що коротке є найкращим показником для незайманої програми.
P. Siehr

@ P.Siehr Що б ви рекомендували замість цього?
Шельваку

Відповіді:


6

Haskell , 132 байти

q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"

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

Це розширення квінти

main=putStr$(++)<*>show$"main=putStr$(++)<*>show$"

яка працює, об'єднуючи рядок даних із цитованою версією (використовуючи show) себе та друкуючи результат. Однак це не є первозданним, оскільки будь-які символи в рядку даних можуть бути видалені без відмови, а також частина $(++)<*>show$або (++)<*>частина може бути скинута без порушення програми.

Щоб виправити це, визначена спеціальна функція друку q, яка перевіряє довжину заданої рядки і викликає, failякщо вона коротша за 132. Це вловлює видалення будь-якої послідовності з рядка даних, а також видалення $(++)<*>show$або (++)<*>, як в обох випадках результуючої рядок, переданий до q, коротший

В q число 132можна скоротити до 1, 13, 32або 2, але в кожному разі знову failвикликається.

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

Редагувати: Дякую Шріану Йохансену та Шельваку за те, що вони вказали на недоліки!


Боюся, fail[]|length x/=122її можна зняти. fail[]:[putStr x|length x==122]може працювати краще.
Ørjan Johansen

Арг, ні, тоді їх |length x==122можна було б зняти. if length x==122 then putStr x else fail[]можливо?
Ørjan Johansen

@ ØrjanJohansen Хороший улов, я був if then elseраніше, але думав, що можу скоротити його.
Laikoni

2
putStr xможе стати p x, що коли я намагався, щоб моя система працювала дуже довго, перш ніж я її вбив, я підозрюю, що рекурсія виклику хвоста була оптимізована, тому це нескінченний цикл. Я не знаю достатньої кількості haskell, щоб дати будь-які пропозиції щодо її виправлення.
Шельваку

@Shelvacu Whoops. Перейменування pна це qмає виправити.
Ørjan Johansen

4

Python 3 , 113 байт

for[]in{113:[]}[open(1,"w").write((lambda s:s%s)('for[]in{113:[]}[open(1,"w").write((lambda s:s%%s)(%r))]:a'))]:a

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

Як це працює

Ми не можемо легко використовувати кілька операторів, оскільки другий може бути видалений, тому ми починаємо з одновиразного quine:

print((lambda s:s%s)('print((lambda s:s%%s)(%r))'))

Для захисту від видалення підрядків ми використовуємо open(1,"w").writeзамість цього print. У Python 3 writeповертає кількість написаних символів, за якими ми перевіримо, 113щоб жодна частина рядка не була видалена. Ми робимо це, шукаючи повернене значення у словнику {113:[]}та перебираючи результат на for[]in…:a, який не вдасться, якщо ми не отримали порожній ітерабельний або якщо forвиписку буде видалено.


1
Чи можете ви пояснити, як працює ваш код?
Шельваку

@Shelvacu Так, додано.
Anders Kaseorg

3

Рубін, 78 байт

eval(*[($>.write((s=%{eval(*[($>.write((s=%%{%s})%%s)-78).chr])})%s)-78).chr])

Я писав це, коли думав про виклик, щоб переконатися, що це можливо. Він використовує ту саму «обгортку» з однієї з моїх відповідей на оригінальний незайманий виклик.

Пояснення:

  • eval(*[ експр ])

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

    Оператор "splat" *дозволяє використовувати масив як аргументи функції. Це також означає, що якщо evalїї видалити, отримана програма є (*[ expr ]) , що не є дійсним рубіном.

  • ($>.write( вул )-78).chr

    $> - це коротка змінна для STDOUT.

    $>.write(foo) пише foo в STDOUT і, що важливо для цього коду, повертає кількість написаних байтів.

    $>.write(foo)-78: Ось 78 тривалість програми, і тому, якщо програма не керована, також буде кількість записаних байтів. Для цього в безрегламентовому випадку це поверне нуль.

    num.chrповертає num як символ, наприклад 0.chrповертає рядок, що містить один нульовий байт. У програмі, що не працює, це дасть рядок з одним нульовим байтом eval, що є дійсною рубіновою програмою, яка є неоперативною.

    Також у програми може бути видалена підрядка такою, що вона є просто eval(*[(78).chr])або eval(*[(8).chr]), що означає, що числова константа не може закінчуватися жодним із чисел (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48 , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95), оскільки вони є кодами ascii для дійсних односимвольних рубінових програм.

  • %{ вул }

    Це менш відомий синтаксис для рядкових літералів в рубіні. Причина, яка використовується тут, полягає {}в тому, що в рядку можуть використовуватися збалансовані пари , тобто цей синтаксис може містити себе. Наприклад, %{foo{bar}}те саме, що "foo{bar}".

  • (s=%{ дані })%s

    Це визначає змінну, sяка є даними цієї quine, як рядок printf.

    Завдання в рубіні повертають те, що було призначено, тож це те саме, що спочатку призначити, sа потім виконатиs%s

    %на рядку - синтатичний цукор для еквівалента рубіну спринту. The%s Значить , де в даних самі дані повинні бути вбудовані.

    Цей біт коду визначає частину даних quine та вбудовує її в себе, щоб створити повний код.


3

Стандартний ML (MLton) , 204 182 189 байт

val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]

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

Для MLton повні програми SML є або виразами, обмеженими і закінченими ;(наприклад print"Hello";print"World";), або деклараціями з ключовими словами varта fun(наприклад var _=print"Hello"var _=print"World"), де_ є шаблоном, який також може бути замінений будь-яким ім'ям змінної.

Перший варіант марний для незайманого програмування, оскільки сам ;по собі є дійсною програмою (яка нічого не робить, але і не помиляється). Проблема другого підходу полягає в тому, що декларації, подібні до цього, var _=print"Hello"можуть бути скорочені до просто var _="Hello"(або навіть var _=print), оскільки декларація зvar працює до тих пір, поки правий бік є дійсним виразом або значенням SML (SML є функціональною мовою, тому функції можуть бути використовується також як значення).

На цей момент я був готовий оголосити незаймане програмування в SML неможливим, коли випадково натрапив на відповідність шаблонів у val-деклараціях. Виявляється, синтаксис для декларацій не є, val <variable_name> = <expression>але val <pattern> = <expression>там, де шаблон може складатися з змінних імен, констант і конструкторів. Оскільки printфункція має тип string -> unit, ми можемо використовувати зіставлення з зразком на unit-value ()для забезпечення , що функція друку фактично застосовується до рядка: val()=print"Hey". При такому підході видалення printабо "Hey"призводить до Pattern and expression disagreeпомилки.

З таким способом первозданної друку під рукою наступним кроком є ​​написання квіта, перш ніж нарешті потрібно додати ще кілька збережень для збереження. Раніше я використовував просту техніку SML quine (див. Історію редагування ), але Андерс Касеорг зазначив інший підхід, який може зберегти кілька байт у його випадку. Він використовує вбудовану String.toStringфункцію для обробки вхідних рядків і має загальну форму <code>"<data>", де "<data>"є рядок, що утворився codeраніше:

val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"

Це робоча квітка, але ще не незаймана. Перш за все Андерс Касеорг з'ясував, що MLton приймає єдину цитату "як код, не створюючи помилок, а це означає, що ми не можемо мати код, що закінчується цитатою, як зазначено вище. Найкоротшим способом запобігти цьому було б загортати все val()=в пару круглих дужок, проте тоді код може бути зведений до val()=(). Другий найкоротший спосіб, який я знайшов - це використовувати val()=hd[ ... ], тобто ми загортаємо все до списку і повертаємо його перший елемент, щоб зробити диспетчером типу щасливим.

Щоб переконатися, що жодна частина рядка даних не може бути видалена, не помітивши, valзнов-таки стане в нагоді відповідність шаблонів у -декларуваннях: Довжина остаточного рядка, який слід надрукувати (і, таким чином, довжина програми), повинна дорівнювати 195, так ми можемо записати let val t=... val 195=size t in print t endв тілі fnабстракції замість print(...). Видалення частини рядка призводить до довжини менше 189, тим самим спричиняючи викид для Bindвиключення.

Залишилося питання: весь val 195=size tчек можна просто зняти. Ми можемо запобігти цьому, розширивши чек на збіг на кортеж:, val t=... val(216,u)=(n+size t,t)in print u endтаким чином, що видалення перевірки призводить до незв'язаної змінної u.

Загалом, це дає наступне 195-байтне рішення:

val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]

Застосування трюку з гольфу щодо використання назв змінних операторів, таких як !, $а не %замість них n, tа також uдля економії білого простору (див. Цю пораду ) призводить до остаточної версії 182 байтів.

Усі інші видалення підрядків, які там, де прямо не вказано в поясненні, повинні призвести до синтаксису або помилки типу.

Правка 1: length(explode t) просто size t.
Редагування 2: Дякуємо Андерсу Касеоргу за різний підхід і вказує на "вразливість".


−2 байти , записавши "\""безпосередньо та використовуючи String.toStringдля втечі.
Андерс Касеорг

Зачекайте, це жахливо: MLton, схоже, приймає програму ", видаючи порожній вихід ( TIO ).
Андерс Касеорг

@AndersKaseorg Хм, це дивно. Однак слід виправити це питання за допомогою іншого let ... in ... end.
Лайконі

@AndersKaseorg Я вирішив проблему, сподіваючись, не вводячи нових "вразливих місць".
Лайконі

Насправді я переглянув, як MLton приймає "програму, і, здається, помилка була виправлена ​​в цій комісії , тому, можливо, ваші 182 або мої 180 є нормальними, доки ви не вкажете невипущену версію MLit Git.
Андерс Касеорг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.