Советы по написанию цитат


30

это программа , которая производит вывод , что идентичные исходный код программы. На этом веб-сайте мы, как правило, заботимся только о правильных квинусах (на момент написания статьи текущее определение гласило : «некоторая часть выходных данных кодируется другой частью программы»).

Какой совет вы бы дали для написания правильных квин или программ с квиноподобными свойствами? Как обычно, каждый совет должен быть в другом ответе.

tips  quine 

Ответы:


14

Используйте evalдля уменьшения необходимости копировать код

Большинство квинов требуют двух копий кода; один должен быть выполнен, другой как данные. Это может в конечном итоге удвоить длину исходного кода, усложнить его поддержку и ухудшить счет, если вы пишете квинну для соревнований по .

Объединение двух копий означает, что одна часть информации должна использоваться для двух целей. Попытка обработать ваш код как данные часто невозможна, и обычно считается обманом, когда это так. Однако обработка данных как кода может быть осуществлена ​​на многих языках с помощью встроенной функции, обычно называемой eval. Таким образом, ваша сущность в основном состоит из хранения основной части сущности в переменной (чтобы вы могли ссылаться на нее более одного раза), а затем оценки этой переменной.

Вот пример того, как это работает (пример написан на Python, но нечто подобное работает во многих других языках):

d='print("d="+repr(d)+"\\neval(d)")'
eval(d)

2
@QPaysTaxes: я стремлюсь сделать мой код максимально удобным для чтения и сопровождения, насколько позволяет условие победы. К сожалению, это все еще, как правило, неотличимо от шума линии ASCII (или просто обычного шума линии, если я использую Jelly) от людей, которые не привыкли к языку.

14

Воспользуйтесь преимуществами форматирования строки

Один из самых простых способов создать квинну - определить строку, а затем поместить строку внутрь себя с форматированием строки.

s='s=%r;print s%%s';print s%s

Таким образом, в этом примере Python quine мы объявляем строку с первой частью, равной тому, что находится перед строкой s=, затем мы разрешаем вставку строки с форматированием с помощью %rи, наконец, помещаем то, что следует после строки, для ее печати и форматирования. , Конечный символ новой строки потому, что printпечатает конечный символ новой строки.

Так что шаблон действительно такой в ​​Python:

<A>'<A>%r<B>'<B>

Чтобы расширить существующую квину с помощью большего количества кода:

<more A>s='<more A>s=%r;print s%%s<more B>';print s%s<more B>

9

Стригированные функции

В нескольких языках функциональные объекты (или эквивалентные конструкции) неявно хранят свой исходный код и возвращают его при преобразовании в строку. Это позволяет создавать компактные квины без использования строки eval . Ярким примером такого языка является JavaScript:

function f(){console.log(f+"f()")}f()

Этот код определяет и вызывает функцию, fкоторая при вызове печатает свой собственный исходный код с последующим вызовом к себе. Единственная часть программы, которая должна дублироваться, - это вызов функции f(). Конечно, тело функции может включать в себя произвольную «полезную нагрузку» кода, которая также будет выполняться при вызове функции.


Более компактная версия того же трюка работает на языках гольфа GolfScript :

{".~"}.~

и CJam :

{"_~"}_~

Каждая из этих квин сначала определяет блок анонимного кода (заключенный в фигурные скобки), который во многом похож на функциональный объект в JavaScript: он может быть выполнен и, если он преобразован в строку, возвращает свой исходный код. Остальная часть кода ( .~в GolfScript или _~в CJam) затем выполняет блок, оставляя его копию в стеке. Затем код внутри блока помещает строку в стек, которая повторяет код за пределами блока. Когда интерпретатор завершает работу, он автоматически выравнивает и печатает все, что осталось в стеке. Как и в примере с JavaScript, эти блоки кода также могут быть выполнены для переноса и выполнения произвольной полезной нагрузки дополнительного кода без необходимости его дублирования.


9

Используйте разделители строк, которые вкладываются без экранирования

Часто одна из самых сложных частей написания лебеды - это шаг к спасению. Это необходимо почти во всех местах; проблема заключается в том, что вы как-то храните данные, и вам нужно скопировать код, который хранит данные в выходных данных квин. Этот код будет содержать экранированную форму данных, поэтому программа увидит неэкранированную форму, и вам нужно будет повторно экранировать ее.

Самый простой способ обработки шага эскейпинга состоит в том, что экранированные и неэкранированные формы данных отличаются только наличием или отсутствием разделителей строк. Таким образом, экранирование является простым делом добавления новой пары разделителей строк вокруг строки. К сожалению, это может сработать, только если сами разделители строк могут быть выражены в данных без экранирования.

Perl - хороший пример языка, на котором работает этот трюк. Хотя его обычные разделители строк - это "…"или '…', менее часто используемые q(…)гнезда, позволяющие записать такой вид квин:

$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print

Это код + данные квин. s///является операцией замены строки регулярного выражения; мы используем 0в качестве маркера и сопоставляем его в регулярном выражении как \d(«любая цифра»), чтобы избежать использования маркера более одного раза (хотя в качестве другой оптимизации мы могли бы просто использовать 0снова, потому что Perl s///заменяет только первое вхождение на дефолт). Обратите внимание, что здесь не требуется явный шаг экранирования, так как q(…)разделители могут быть просто включены буквально в строку данных.


8

Код + данные квин

Самая общая структура для квайна выглядит примерно так: псевдокод:

data = " экранированная версия всей программы,
        с этой строкой заменяется маркером "
program = data.replace (
  выражение, которое оценивает маркер, но не упоминает его ,
  экранированы (данные))
программа печати;

Эта структура может использоваться для написания (довольно наивного) квин на большинстве языков. Однако на большинстве скоринговых систем он имеет довольно плохие оценки, потому что вам приходится писать всю программу дважды. Тем не менее, большинство структур Quine можно считать оптимизацией этой.

В этом есть некоторые тонкости. В некоторых языках самой сложной частью выполнения этой операции является написание экранирующего кода; на многих языках изготовление маркера без упоминания его названия затруднительно; и в некоторых эзотерических языках вам придется изобретать свой собственный тип строкового литерала. Все три операции, как правило, не вызывают особых проблем.

Например, мы можем написать квинту Python, избегая строки, используя reprи используя 2-символьную x"строку последовательности (которая представляется как "x\"", т.е. не используя последовательность x"в строковом представлении самой строки) в качестве маркера:

d='d=x"\nprint(str.replace(d,"x\\"",repr(d)))'
print(str.replace(d,"x\"",repr(d)))

2
Может быть, стоит отметить (возможно, в другом ответе), что вставка строки в позицию маркера часто бывает дорогой в esolangs, и может стоить структурировать код так, чтобы сама строка была первой или последней (может быть отделена от конец одним или двумя символами, которые вы можете жестко закодировать), чтобы вы знали, куда он должен идти.
Мартин Эндер,

@MartinEnder: Я согласен, что стоит упомянуть, но это, вероятно, другой ответ (а не комментарий или редактирование в этом ответе). Большинство советов по квине являются модификациями этой общей структуры, поэтому я хотел сначала показать ее в качестве подсказки, так как многие люди не имеют ни малейшего представления, с чего начать писать квайн.

Альтернативой маркеру является использование двух строк, я сделал это для стекла .
Орджан Йохансен

4

Эксплойт обёртывание исходного кода

На многих языках (в основном это двумерные языки) исходный код можно обернуть; при определенных обстоятельствах (например, в Befunge-98, если ваша программа однострочная), если вы пройдете через конец программы, вы вернетесь к началу программы. Нелинейное поведение такого типа означает, что вы можете писать код внутри и вне строкового литерала одновременно; непревзойденный "(или какой бы то ни было строковый разделитель) будет эффективно давать вам строку, содержащую всю остальную часть программы (за исключением "самой).

Одна из проблем, связанных с использованием этого трюка, заключается в том, что вы получите строку с точки зрения ", а не с самого начала программы (как вам бы того хотелось). Таким образом, вероятно, проще всего переставить программу так, чтобы она "появлялась в начале или в конце. Это часто означает разделение вашей программы на несколько частей и использование любых интересных / необычных команд управления потоком, которые есть в вашем языке (большинство языков, которые позволяют строковым литералам обтекать программу, имеют хороший выбор из них).

Хороший пример - Quine @ Justin's Befunge-98 :

<@,+1!',k9"

Непревзойденный "в конце программы оборачивает всю программу в строковый литерал, поэтому (для запуска справа налево из- <за начала) все, что нам нужно сделать, это вывести программу ( 9k), затем вывести двойную кавычку ( '!1+,) и выход ( @). Это позволяет сэкономить две копии программы (одна как код, другая как данные); две копии - это один и тот же кусок кода, просто интерпретируемый по-разному.


4

Помните строение квин

Мне нравится думать о кваинах как о трех частях, а не 2:

  • Часть 1: Генерация представления данных частей 2 и 3.
  • Часть 2: Используйте данные для алгоритмической печати части 1.
  • Часть 3: Расшифруйте представление для печати частей 2 и 3.

Это может облегчить размышления о киве. Вот Python quine, каждая строка которого соответствует части:

s = "print(\"s = \" + repr(s))\nprint(s)"
print("s = " + repr(s))
print(s)

Иногда вы используете evalили подобное для удаления дублирования, но в целом я обнаружил, что это помогает в написании простых цитат.

Давайте посмотрим на две разные квоты недогрузки. Это первое:

(:aSS):aSS

Первая часть (:aSS), которая генерирует представление данных. Второй :aS, который печатает (:aSS). Третья часть S, которая печатает :aSS.

Вот вторая четверка:

(:aS(:^)S):^

На первый взгляд, это не подходит. Но если вы расширите квин, вы получите:

(:aS(:^)S):aS(:^)S

Теперь (:aS(:^)S)часть 1, :aSчасть 2 и (:^)Sчасть 3.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.