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
- fun
chave 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 var
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>
mas val <pattern> = <expression>
, onde um padrão pode consistir em nomes de variáveis, constantes e construtores. À medida que a print
funçã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 print
ou "Hey"
resulta em um Pattern 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 do code
anterior:
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 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 escrever let val t=... val 195=size t in print t end
no corpo da fn
abstração em vez de print(...)
. A remoção de uma parte da cadeia resulta em um comprimento menor que 189, fazendo com que uma Bind
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:, de val 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 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
, t
e u
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)
é justo size t
.
Edit 2: Obrigado a Anders Kaseorg por uma abordagem diferente do quine e por apontar uma "vulnerabilidade".