Dicas para escrever quines


30

Um é um programa que produz uma saída idêntica ao código-fonte do programa. Neste site, geralmente nos preocupamos apenas com quines adequados (no momento da redação, a definição atual é "uma parte da saída é codificada por uma parte diferente do programa").

Que conselho você daria para escrever quines adequados ou programas com propriedades semelhantes a quine? Como sempre, cada dica deve ter uma resposta diferente.

tips  quine 

Respostas:


14

Use evalpara reduzir a necessidade de copiar código

A maioria dos quines exige duas cópias do código; um a ser executado, outro como dados. Isso pode dobrar o tamanho do código-fonte, dificultar a manutenção e piorar a pontuação se você estiver escrevendo o quine para uma competição de .

A mesclagem das duas cópias significa que uma informação precisa ser usada para dois propósitos. Tentar tratar seu código como dados geralmente não é possível e, normalmente, é considerado trapaça. Tratar dados como código, no entanto, pode ser feito em vários idiomas através do uso de um built-in, normalmente chamado eval. Como tal, seu quine consiste basicamente em armazenar o corpo principal do quine em uma variável (para que você possa consultá-lo mais de uma vez) e depois avaliar essa variável.

Aqui está um exemplo de como isso funciona (o exemplo está escrito em Python, mas algo semelhante funciona em muitas outras linguagens):

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

2
@QPaysTaxes: pretendo tornar meu código aqui o mais legível e possível de manter, conforme a condição da vitória permitir. Infelizmente, isso ainda é indistinguível do ruído da linha ASCII (ou apenas do ruído da linha comum, se eu estiver usando o Jelly) de pessoas que não estão acostumadas com o idioma.

14

Aproveite a formatação de string

Uma das maneiras mais fáceis de criar um quine é definir uma sequência e colocar a sequência dentro de si com a formatação da sequência.

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

Portanto, neste exemplo Python quine, declaramos uma string com a primeira parte igual ao que está antes da string s=, depois permitimos que a string seja inserida com formatação e %r, finalmente, colocamos o que vem depois da string para imprimi-la e formatá-la. . A nova linha final é porque printimprime uma nova linha final.

Portanto, o modelo é realmente isso, em Python:

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

Para expandir o quine existente com mais código:

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

9

Funções Stringified

Em várias linguagens, objetos de função (ou construções equivalentes) armazenam implicitamente seu código-fonte e o retornam quando convertidos em uma string. Isso permite quines compactos sem usar a avaliação de string . Um exemplo notável dessa linguagem é o JavaScript:

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

Esse código define e chama uma função fque, quando chamada, imprime seu próprio código-fonte seguido por uma chamada para si mesmo. A única parte do programa que precisa ser duplicada é a chamada de função f(). Obviamente, o corpo da função pode incluir uma "carga útil" arbitrária de código que também será executada quando a função for chamada.


Uma versão mais compacta do mesmo truque funciona nas linguagens de golfe GolfScript :

{".~"}.~

e CJam :

{"_~"}_~

Cada um desses quines define primeiro um bloco de código anônimo (entre chaves), que se comporta como um objeto de função no JavaScript: ele pode ser executado e, se for string, retorna seu código-fonte. O restante do código ( .~no GolfScript ou _~no CJam) executa o bloco, deixando uma cópia na pilha. O código dentro do bloco envia uma string para a pilha que repete o código fora do bloco. Quando o intérprete sai, ele sequencia e imprime automaticamente tudo o que resta na pilha. Como no exemplo do JavaScript, esses blocos de código também podem ser feitos para transportar e executar uma carga arbitrária de código adicional sem a necessidade de duplicá-lo.


9

Use delimitadores de cadeia que aninham sem escapar

Muitas vezes, uma das partes mais difíceis de escrever um quine é a etapa de fuga. Isso é necessário em quase todas as situações; o problema é que você está armazenando dados de alguma forma e precisa replicar o código que armazena os dados na saída do quine. Esse código conterá uma forma de escape dos dados; portanto, o programa verá um formulário sem escape e você precisará recuperá-lo novamente.

A maneira mais fácil de lidar com a etapa sem escape é se as formas com e sem escape dos dados diferem apenas na presença ou ausência de delimitadores de cadeia. Escapar é, portanto, uma simples questão de adicionar um novo par de delimitadores de string ao redor da string. Infelizmente, isso claramente só funciona se os próprios delimitadores de string puderem ser expressos nos dados sem escapar.

Perl é um bom exemplo de uma linguagem onde esse truque funciona. Embora seus delimitadores de cadeia usuais sejam "…"ou '…', os q(…)ninhos menos usados , permitindo que esse tipo de solução seja escrita:

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

Este é um código + dados quine. s///é uma operação de substituição de cadeia de caracteres regex; usamos 0como marcador e o correspondemos no regex como \d("qualquer dígito"), para evitar o uso do marcador mais de uma vez (embora, como outra otimização, poderíamos apenas ter usado 0novamente, porque o Perl s///substitui apenas a primeira ocorrência por padrão). Observe que não há etapa de escape explícita necessária aqui, pois os q(…)delimitadores podem ser simplesmente incluídos literalmente na cadeia de dados.


8

Código + dados quines

A estrutura mais geral para um quine se parece com esse pseudocódigo:

data = " uma versão escapada de todo o programa,
        com essa string substituída por um marcador "
program = data.replace (
  uma expressão que é avaliada para o marcador, mas não menciona ,
  escapado (dados))
programa de impressão;

Essa estrutura pode ser usada para escrever um quine (bastante ingênuo) na maioria dos idiomas. No entanto, ele tende a pontuar bastante na maioria dos sistemas de pontuação, porque você precisa escrever a totalidade do programa duas vezes. No entanto, a maioria das estruturas quine pode ser considerada otimização dessa.

Existem algumas sutilezas nisso. Em alguns idiomas, a parte mais difícil de executar esta operação é escrever o código de escape; em muitas línguas, produzir o marcador sem mencionar seu nome é difícil; e em algumas linguagens esotéricas, você terá que inventar seu próprio tipo de literal de string. Todas as três operações tendem a não causar muitos problemas, no entanto.

Por exemplo, podemos escrever uma coluna Python escapando de uma string usando repre usando a sequência de 2 x"caracteres (que é representável como "x\"", ou seja, não usando a sequência x"na representação de string da própria string) como marcador:

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

2
Pode valer a pena notar (possivelmente em outra resposta) que inserir a string na posição de um marcador geralmente é caro em esolangs, e pode valer a pena estruturar o código de modo que a própria string seja a primeira ou a última coisa (talvez separada de o final com um ou dois caracteres, que você pode codificar) para que você saiba para onde ele deve ir.
Martin Ender

@ MartinEnder: Concordo que vale a pena mencionar, mas provavelmente é outra resposta (em vez de um comentário ou uma edição nesta resposta). A maioria das dicas de quine são modificações nessa estrutura geral, então eu queria divulgá-la como uma dica por conta própria primeiro, pois muitas pessoas não têm idéia de por onde começar para escrever uma quine.

Uma alternativa a um marcador é usar duas strings, eu fiz isso no Glass .
Ørjan Johansen

4

Explorar código fonte de quebra automática

Em algumas linguagens (principalmente linguagens 2D), o código fonte pode ser contornado; sob certas circunstâncias (por exemplo, no Befunge-98, se o seu programa for de uma linha) após o final do programa, você voltará ao início do programa. Esse tipo de comportamento não linear significa que você pode escrever um código dentro e fora de uma string literal ao mesmo tempo; um inigualável "(ou qualquer que seja o delimitador de cadeia de caracteres) fornecerá efetivamente uma cadeia de caracteres contendo todo o restante do programa (exceto o "próprio).

Um problema ao usar esse truque é que você obterá a string como vista do ponto de vista do ", e não desde o início do programa (como você gostaria). Como tal, é provavelmente mais fácil reorganizar o programa para que "apareça no início ou no final. Isso geralmente significa dividir seu programa em várias partes e fazer uso de qualquer comando de controle de fluxo interessante / incomum que sua linguagem possua (a maioria das linguagens que permite que os literais de cadeia de caracteres envolvam o programa tem uma boa seleção).

Um bom exemplo é o bine de Justin Befunge-98 :

<@,+1!',k9"

O inigualável "no final do programa envolve o programa inteiro em uma literal de cadeia de caracteres, então (executando da direita para a esquerda devido ao <início) tudo o que precisamos fazer é produzir o programa ( 9k), em seguida, aspas duplas ( '!1+,) e exit ( @). Isso economiza a necessidade de duas cópias do programa (uma como código, uma como dados); as duas cópias são o mesmo pedaço de código, apenas interpretadas de maneiras diferentes.


4

Lembre-se da estrutura de um Quine

Eu gosto de pensar em quines como três partes, em vez de 2:

  • Parte 1: Gere uma representação de dados das partes 2 e 3.
  • Parte 2: use os dados para imprimir algoritticamente a parte 1.
  • Parte 3: decodifique a representação para imprimir as partes 2 e 3.

Isso pode facilitar a reflexão sobre os quines. Aqui está um Python quine, com cada linha correspondente a uma parte:

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

Às vezes, você usa um evalou similar para remover a duplicação, mas geralmente descobri que isso ajuda a escrever quines simples.

Vamos dar uma olhada em dois tipos diferentes de Underload. Este é o primeiro:

(:aSS):aSS

A primeira parte é (:aSS), que gera a representação dos dados. O segundo é :aS, que imprime (:aSS). A terceira parte é S, que imprime :aSS.

Aqui está o segundo quine:

(:aS(:^)S):^

No começo, isso não parece se encaixar. Mas se você expandir o quine, obtém:

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

Agora (:aS(:^)S)faz parte 1, :aSfaz parte 2 e (:^)Sfaz parte 3.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.