Esse desafio se baseia na pergunta de Helka Homba : Programming a Pristine World . A partir dessa pergunta, a definição de um programa primitivo é:
Vamos definir um programa intocado como um programa que não possui nenhum erro, mas com erro se você modificá-lo removendo qualquer substring contíguo de N caracteres, onde
1 <= N < program length
.Por exemplo, o programa Python 2 de três caracteres
`8`
é um programa primitivo ( obrigado, Sp ) porque todos os programas resultantes da remoção de substrings de comprimento 1 causam erros (na verdade, erros de sintaxe, mas qualquer tipo de erro provoca):
8` `` `8
e também todos os programas resultantes da remoção de substrings de comprimento 2 causam erros:
` `
Se, por exemplo,
`8
tivesse sido um programa sem erros,`8`
não seria intocado, porque todos os resultados da remoção de substring devem ter erro.Notas:
- Os avisos do compilador não contam como erros.
- Os subprogramas com erro podem receber entrada ou fornecer saída ou fazer qualquer outra coisa, desde que cometerem erros, não importa o que eventualmente.
Sua tarefa é criar um programa de comprimento diferente de zero, que imprima exatamente seu próprio código-fonte, siga as regras para um quine adequado e seja puro.
A resposta mais curta em bytes para cada idioma vence.
Respostas:
Haskell , 132 bytes
Experimente online!
Esta é uma extensão do quine
que funciona concatenando a sequência de dados com uma versão citada (usando
show
) de si mesma e imprimindo o resultado. No entanto, isso não é primitivo, pois qualquer caractere na cadeia de dados pode ser removido sem falhar e também a parte$(++)<*>show$
ou(++)<*>
pode ser eliminada sem a interrupção do programa.Para corrigir isso,
q
é definida uma função de impressão personalizada que verifica o comprimento da sequência especificada e chamafail
se ela é menor que 132. Isso captura a remoção de qualquer sequência da sequência de dados e também as remoções de$(++)<*>show$
ou(++)<*>
, como em ambos os casos, a resultante a cadeia passada paraq
é mais curta.q
No número132
poderia ser reduzido para1
,13
,32
ou2
, mas em cada caso novamentefail
é chamado.Até onde eu sei, a remoção de qualquer outra substring causa um erro de sintaxe ou de tipo, portanto o programa nem sequer compila em primeiro lugar. (O sistema de tipo estrito de Haskell é útil aqui.)
Edit: Obrigado a Ørjan Johansen e Shelvacu por apontar falhas!
fonte
fail[]|length x/=122
possa ser removido.fail[]:[putStr x|length x==122]
pode funcionar melhor.|length x==122
poderia ser removido.if length x==122 then putStr x else fail[]
possivelmente?if then else
antes, mas pensei que poderia encurtá-lo.putStr x
pode se tornarp x
, que quando tentei no meu sistema funcionou por muito tempo antes de matá-lo, suspeito que a recursão da chamada de cauda foi otimizada, por isso é um loop infinito. Não conheço haskell suficiente para dar sugestões sobre como corrigi-lo.p
paraq
deve corrigir isso.Python 3 , 113 bytes
Experimente online!
Como funciona
Não podemos usar facilmente várias instruções, pois a segunda pode ser excluída; portanto, começamos com um quine de expressão única:
Para protegê-lo contra exclusões de substring, usamos em
open(1,"w").write
vez deprint
. No Python 3,write
retorna o número de caracteres escritos, que iremos verificar113
para garantir que nenhuma parte da string tenha sido excluída. Fazemos isso pesquisando o valor de retorno no dicionário{113:[]}
e repetindo o resultado comfor[]in…:a
, que falhará se não obtivermos uma iterável vazia ou se afor
instrução for excluída.fonte
Ruby, 78 bytes
Escrevi isso quando pensei no desafio para garantir que era possível. Ele usa o mesmo "invólucro" de uma das minhas respostas para o desafio original.
Explicação:
eval(*[
expr])
Isso avalia o código retornado como um programa ruby. Isso efetivamente testa se a string retornada pelo código é um programa ruby válido. Convenientemente, os programas ruby podem ficar em branco ou consistir apenas de espaço em branco.
O operador "splat"
*
permite que você use uma matriz como argumentos para uma função. Isso também significa que, seeval
for removido, o programa resultante será(*[
expr])
, que não é válido para ruby.($>.write(
str)-78).chr
$>
é uma variável curta para STDOUT.$>.write(foo)
escreve foo em STDOUT e, importante para esse código, retorna o número de bytes gravados.$>.write(foo)-78
: Aqui78
está o tamanho do programa e, se o programa não for mutilado, também será o número de bytes gravados. Portanto, no caso não manipulado, isso retornará zero.num.chr
retorna num como um caractere; por exemplo0.chr
, retornará uma string contendo um único byte nulo. No programa não manipulado, isso fornecerá uma string com um único byte nulo paraeval
, que é um programa ruby válido e não é operacional.Além disso, o programa pode ter uma subcadeia removida de forma que seja justa
eval(*[(78).chr])
oueval(*[(8).chr])
, o que significa que a constante numérica não pode terminar com nenhum dos números (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48 , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95) porque são códigos ASCII para programas ruby válidos de um único caractere.%{
str}
Essa é uma sintaxe menos conhecida para literais de strings em ruby. A razão pela qual é usada aqui é que pares balanceados de
{}
podem ser usados na cadeia, o que significa que essa sintaxe pode se conter. Por exemplo,%{foo{bar}}
é o mesmo que"foo{bar}"
.(s=%{
dados})%s
Isso define a variável
s
que são os dados desse quine, como uma string printf.As atribuições em ruby retornam o que foi atribuído, portanto é o mesmo que primeiro atribuir
s
e depois executars%s
%
em uma corda é açúcar sintático para o equivalente em rubi do sprintf. Os%s
significa que dentro dos dados dos próprios dados devem ser incorporados.Esse bit de código define a parte de dados do quine e a incorpora em si mesma para criar o código completo.
fonte
ML padrão (MLton) ,
204182189 bytesExperimente online!
Para o MLton, os programas SML completos são expressões delimitadas e finalizadas por
;
( por exemploprint"Hello";print"World";
) ou declarações com as palavrasvar
-fun
chave e (por exemplovar _=print"Hello"var _=print"World"
) onde_
é um curinga que também pode ser substituído por qualquer nome de variável.A primeira opção é inútil para a programação original, porque
;
por si só é um programa válido (que não faz nada, mas também não erra). O problema com a segunda abordagem é que declarações comovar _=print"Hello"
podem ser encurtadas para apenasvar _="Hello"
(ou até mesmovar _=print
) porque a declaraçãovar
funciona desde que o lado direito seja uma expressão ou valor SML válido (SML é uma linguagem funcional, portanto, as funções podem ser usado como valores também).Nesse ponto, eu estava pronto para declarar impossível a programação primitiva no SML, quando por acaso me deparei com a correspondência de padrões nas
val
declarações -. Acontece que a sintaxe para declarações não éval <variable_name> = <expression>
masval <pattern> = <expression>
, onde um padrão pode consistir em nomes de variáveis, constantes e construtores. À medida que aprint
função tem tipostring -> unit
, podemos usar uma correspondência de padrão naunit
-valor()
para impor que a função de impressão é realmente aplicado à string:val()=print"Hey"
. Com essa abordagem, a remoção de umprint
ou"Hey"
resulta em umPattern and expression disagree
erro.Com esta forma de impressão impecável em mãos, o próximo passo é escrever um quine, antes de finalmente ser necessário adicionar mais salvaguardas. Anteriormente, usei uma técnica fácil de SML Quine (consulte o histórico de revisões ), mas Anders Kaseorg apontou uma abordagem diferente que pode economizar alguns bytes no seu caso. Ele usa a
String.toString
função interna para lidar com escape de string e é da forma geral<code>"<data>"
, onde"<data>"
é um string escapado docode
anterior:Esta é uma solução prática, mas ainda não intocada. Antes de tudo, Anders Kaseorg descobriu que o MLton aceita uma única citação
"
como código sem gerar erros, o que significa que não podemos ter o código terminado em uma citação como acima. A maneira mais curta de evitar isso seria agrupar tudo depoisval()=
entre parênteses, no entanto, o código poderia ser reduzido paraval()=()
. A segunda maneira mais curta que encontrei é usarval()=hd[ ... ]
, ou seja, agrupamos tudo em uma lista e retornamos seu primeiro elemento para deixar o verificador de tipos feliz.Para garantir que nenhuma parte da sequência de dados possa ser removida sem ser notada, as
val
declarações de correspondência de padrões são úteis novamente: O comprimento da sequência final a ser impressa (e, portanto, a duração do programa) deve ser igual a 195, portanto podemos escreverlet val t=... val 195=size t in print t end
no corpo dafn
abstração em vez deprint(...)
. A remoção de uma parte da cadeia resulta em um comprimento menor que 189, fazendo com que umaBind
exceção seja lançada.Ainda resta um problema: todo o
val 195=size t
cheque pode ser simplesmente descartado. Podemos evitar isso expandindo a verificação para corresponder a uma tupla:, deval t=... val(216,u)=(n+size t,t)in print u end
modo que a remoção da verificação resulte em uma variável não acopladau
.No total, isso gera a seguinte solução de 195 bytes:
A aplicação do truque de golfe ao usar nomes de variáveis de operador como
!
,$
e em%
vez den
,t
eu
para economizar espaço em branco (consulte esta dica ), leva à versão final de 182 bytes.Todas as outras remoções de substring que não foram explicitamente declaradas na explicação devem resultar em erro de sintaxe ou de tipo.
Editar 1:
length(explode t)
é justosize t
.Edit 2: Obrigado a Anders Kaseorg por uma abordagem diferente do quine e por apontar uma "vulnerabilidade".
fonte
"\""
diretamente e usandoString.toString
para escapar."
, produzindo saída vazia ( TIO ).let ... in ... end
."
como um programa, e parece que o bug foi corrigido nesse commit , então talvez o seu 182 ou meu 180 esteja bom desde que você especifique a versão não lançada do Git do MLton.