Criando um mundo primitivo

16

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, `8tivesse 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.

Shelvacu
fonte
Estou assumindo que idiomas que não erro não podem competir?
ATaco 11/07
@ATaco Infelizmente sim. Outros idiomas, como o lisp, têm a sintaxe estruturada de tal maneira que tornar impossível um programa útil útil.
Shelvacu
RIP Realmente / Sério
ATaco
'A resposta mais curta em bytes para cada idioma vence.' Não tenho certeza de que curto seja a melhor medida para um programa intocado.
P. Siehr
@ P.Siehr O que você recomendaria?
Shelvacu

Respostas:

6

Haskell , 132 bytes

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$"

Experimente online!

Esta é uma extensão do quine

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

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 chama failse 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 para qé mais curta.

qNo número 132poderia ser reduzido para 1, 13, 32ou 2, mas em cada caso novamente failé 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!

Laikoni
fonte
Receio que fail[]|length x/=122possa ser removido. fail[]:[putStr x|length x==122]pode funcionar melhor.
Ørjan Johansen
Argh, não, então |length x==122poderia ser removido. if length x==122 then putStr x else fail[]possivelmente?
Ørjan Johansen
@ ØrjanJohansen Boa captura, eu tinha um if then elseantes, mas pensei que poderia encurtá-lo.
Laikoni 11/07
2
putStr xpode se tornar p 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.
Shelvacu
@Shelvacu Whoops. Renomear ppara qdeve corrigir isso.
Ørjan Johansen
4

Python 3 , 113 bytes

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

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:

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

Para protegê-lo contra exclusões de substring, usamos em open(1,"w").writevez de print. No Python 3, writeretorna o número de caracteres escritos, que iremos verificar 113para 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 com for[]in…:a, que falhará se não obtivermos uma iterável vazia ou se a forinstrução for excluída.

Anders Kaseorg
fonte
1
Você poderia dar uma explicação de como seu código funciona?
21417 Shelvacu
@Shelvacu Sim, acrescentou.
Anders Kaseorg 17/07
3

Ruby, 78 bytes

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

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, se evalfor 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: Aqui 78está 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.chrretorna num como um caractere; por exemplo 0.chr, retornará uma string contendo um único byte nulo. No programa não manipulado, isso fornecerá uma string com um único byte nulo para eval, 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])ou eval(*[(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 sque 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 se depois executars%s

    %em uma corda é açúcar sintático para o equivalente em rubi do sprintf. Os %ssignifica 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.

Shelvacu
fonte
3

ML padrão (MLton) , 204 182 189 bytes

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)"]

Experimente online!

Para o MLton, os programas SML completos são expressões delimitadas e finalizadas por ;( por exemplo print"Hello";print"World";) ou declarações com as palavras var- funchave e (por exemplo var _=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 como var _=print"Hello"podem ser encurtadas para apenas var _="Hello"(ou até mesmo var _=print) porque a declaração varfunciona 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 valdeclarações -. Acontece que a sintaxe para declarações não é val <variable_name> = <expression>mas val <pattern> = <expression>, onde um padrão pode consistir em nomes de variáveis, constantes e construtores. À medida que a printfunção tem tipo string -> unit, podemos usar uma correspondência de padrão na unit-valor ()para impor que a função de impressão é realmente aplicado à string: val()=print"Hey". Com essa abordagem, a remoção de um printou "Hey"resulta em um Pattern and expression disagreeerro.

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.toStringfunção interna para lidar com escape de string e é da forma geral <code>"<data>", onde "<data>"é um string escapado do codeanterior:

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

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 depois val()=entre parênteses, no entanto, o código poderia ser reduzido para val()=(). A segunda maneira mais curta que encontrei é usar val()=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 valdeclaraçõ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 escrever let val t=... val 195=size t in print t endno corpo da fnabstração em vez de print(...). A remoção de uma parte da cadeia resulta em um comprimento menor que 189, fazendo com que uma Bindexceção seja lançada.

Ainda resta um problema: todo o val 195=size tcheque pode ser simplesmente descartado. Podemos evitar isso expandindo a verificação para corresponder a uma tupla:, de val t=... val(216,u)=(n+size t,t)in print u endmodo que a remoção da verificação resulte em uma variável não acoplada u.

No total, isso gera a seguinte solução de 195 bytes:

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)"]

A aplicação do truque de golfe ao usar nomes de variáveis ​​de operador como !, $e em %vez de n, te upara 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) é justo size t.
Edit 2: Obrigado a Anders Kaseorg por uma abordagem diferente do quine e por apontar uma "vulnerabilidade".

Laikoni
fonte
-2 bytes escrevendo "\""diretamente e usando String.toStringpara escapar.
Anders Kaseorg
Espere, isso é horrível: o MLton parece aceitar o programa ", produzindo saída vazia ( TIO ).
Anders Kaseorg
@ AndersKaseorg Huh, isso é estranho. No entanto, deve ser possível corrigir esse problema usando outro let ... in ... end.
Laikoni
@AndersKaseorg Corrigi o problema, espero sem introduzir novas "vulnerabilidades".
Laikoni
Na verdade, olhei para o MLton aceitando "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.
Anders Kaseorg