Porque não podemos obter o suficiente de golfe esotérico, podemos?
/// - barras pronunciadas - é uma linguagem divertida, baseada na s///
função de substituição de expressões regulares da fama do Perl. Ele contém apenas dois caracteres especiais, barra /
e barra invertida \
. Você pode encontrar um artigo completo no wiki do esolangs , mas reproduzirei uma descrição do idioma abaixo, além de alguns exemplos.
Em resumo, ele funciona identificando /pattern/repl/rest
no programa e fazendo a substituição quantas vezes for possível. Nenhum caractere é especial, exceto /
e \
: /
demarca padrões e substituições no programa, enquanto \
permite inserir caracteres /
ou literais \
no seu código. Notavelmente, essas não são expressões regulares, apenas substituições simples de strings.
Seu desafio é produzir um intérprete para a linguagem ///, como um programa que lê STDIN ou uma função que usa um argumento de string, com o menor número de caracteres possível.
Você pode usar qualquer idioma, exceto o ///. Você não pode usar nenhuma biblioteca que interprete ///; você pode, no entanto, usar expressões regulares, bibliotecas de expressões regulares ou bibliotecas de correspondência de cadeias.
Execução
Existem quatro estados, impressão , padrão , substituição e substituição . Em todos os estados, exceto substituição :
- Se o programa estiver vazio, a execução será interrompida.
- Senão, se o primeiro caractere for
\
, faça algo com o próximo caractere (se presente) e remova ambos do programa. - Senão, se o primeiro caractere for
/
, remova-o e mude para o próximo estado. - Senão, faça algo com o primeiro caractere e remova-o do programa.
- Repetir.
Os estados alternam entre impressão , padrão , substituição e substituição em ordem.
- No modo de impressão , 'fazer alguma coisa' significa gerar o caractere.
- No modo padrão , 'fazer alguma coisa' significa adicionar o caractere ao padrão atual.
- No modo de substituição , 'fazer alguma coisa' significa adicionar o caractere à Substituição atual.
No modo de substituição , você segue um conjunto diferente de regras. Substitua repetidamente a primeira ocorrência do padrão atual pela substituição atual no programa, até que não sejam possíveis mais substituições. Nesse ponto, limpe o Padrão e a Substituição e retorne ao modo de impressão .
No programa /foo/foobar/foo foo foo
, acontece o seguinte:
/foo/foobar/foo foo foo
foo foo foo
foobar foo foo
foobarbar foo foo
foobarbarbar foo foo
...
Isso faz um loop para sempre e nunca sai do modo de substituição . Da mesma forma, se o Padrão estiver vazio, a primeira ocorrência da sequência vazia - no início do programa - sempre corresponderá, portanto o modo de substituição será repetido para sempre, sem parar.
Exemplos
no
Saída: no
.
/ world! world!/Hello,/ world! world! world!
Saída: Hello, world!
.
/foo/Hello, world!//B\/\\R/foo/B/\R
Saída: Hello, world!
.
a/ab/bbaa/abb
Saída: a
. Programa não para.
//
Saída: nenhuma.
///
Saída: nenhuma. Programa não para.
/\\/good/\/
Saída: good
.
Há também um quine no wiki que você pode tentar.
fonte
/-/World//--/Hello//--W/--, w/---!
O que há para não amar? (Tente remover traços do final)\
caractere escapa de qualquer caractere que o segue, inclusive/
, que pode ser usado posteriormente normalmente. Embora isso não pareça muito, isso faz com que o /// Turing seja completo .///
IDE que estou criando!Respostas:
APL (133)
Essa é uma função que aceita o
///
código como argumento correto.Ungolfed, com explicação:
fonte
///
e//foo/
(ou seja, faz um loop para sempre)?/
, ainda seria deixado naquele ponto.J -
181190170 charIsso foi um pesadelo. Eu o reescrevi do zero, duas vezes, porque ele continuava me incomodando. Esta é uma função que recebe um argumento de cadeia única, produzindo para STDOUT.
Para explicar, vou dividir em subexpressões.
i
(abreviação de iterate ) é um advérbio. Ele pega um argumento do verbo à esquerda e retorna um verbo(f)i
, que quando aplicado a um argumento, se aplicaf
repetidamente ao argumento até que uma das duas coisas aconteça: ele encontra um ponto fixo (y = f y
) ou gera um erro. O comportamento do ponto fixo é inerente^:_
e::]
manipula os erros.parse
tokeniza a entrada no que chamo de forma semi-analisada e, em seguida, corta-a no '/' sem escape. Ele vincula barras invertidas aos personagens, mas não se livra das barras invertidas - para que possamos revertê-lo ou finalizá-lo, dependendo do que queremos.A maior parte do trabalho interessante ocorre em
;:
. Este é um primitivo interpretador de máquina seqüencial, tendo uma descrição da máquina ((0;(0,:~1 0,.2);'\';&<1 0)
) à esquerda e algo para analisar à direita. Isso faz a tokenização. Observarei que essa máquina específica realmente trata o primeiro caractere não-especial, mesmo que seja um\
e deve ser vinculado. Faço isso por alguns motivos: (1) a tabela de estados é mais simples, para que possa ser jogada ainda mais; (2) podemos facilmente adicionar um caractere fictício à frente para evitar o problema; e (3) esse caractere fictício é meio analisado sem nenhum custo extra, para que eu possa usá-lo para configurar a fase de corte, a seguir.Também usamos
<;._1
para cortar o resultado tokenizado em sem escape/
(que é o que eu escolhi para ser o primeiro caractere). Isso é útil para extrair a saída, o padrão e a substituição deout/patt/repl/rest
uma só etapa, mas infelizmente também corta o restante do programa, onde precisamos que eles/
permaneçam intocados. Eu as costuro de volta duranteeval
, porque fazê<;._1
-las em paz acaba custando muito mais.O fork é
(eval [ print)
executadoprint
no resultado deparse
seus efeitos colaterais e, em seguida, é executadoeval
.print
é um verbo simples que abre a primeira caixa (a que sabemos com certeza é a saída), termina de analisá-la e a envia para STDOUT. No entanto, também temos a chance de definir um verbo utilitáriop
.p
é definido como>@{.@[
, então ele pega seu arg esquerdo (age como a identidade se receber apenas um argumento), pega o primeiro item dele (identidade quando recebe um escalar) e desmarca-o (caixa de identidade se já estiver fora da caixa). Isso será muito útil nosub
.eval
avalia o restante do programa processado. Se não tivermos um padrão completo ou uma substituição completa, oeval
jogue fora e retorne uma lista vazia, que finaliza a avaliação cometendo um erro;:
(deparse
) na próxima iteração. Senão,eval
analisa completamente o padrão e a substituição, corrige o restante da fonte e passa os dois parasub
. Por explosão:sub
é onde uma rodada (possivelmente infinita) de substituições acontece. Por causa da maneira como configuramoseval
, a fonte é o argumento certo e o padrão e a substituição são agrupados à esquerda. Como os argumentos são ordenados assim e sabemos que o padrão e a substituição não mudam dentro de uma rodada de substituições, podemos usar outro recurso dei
- o fato de que ele modifica apenas o argumento certo e continua passando na mesma esquerda - para delegar para a necessidade de se preocupar em acompanhar o estado.Existem dois pontos de problemas, no entanto. A primeira é que os verbos J podem ter no máximo dois argumentos, portanto, não temos uma maneira fácil de acessar os que estão agrupados, como padrão e substituição, aqui. Através do uso inteligente do
p
utilitário que definimos, esse não é um grande problema. De fato, podemos acessar o padrão em um caractere, apenas usandop
, devido à sua>@{.@[
definição: a Unbox do primeiro item do argumento Esquerda. Conseguir a substituição é mais complicado, mas a maneira mais curta seriap&|.
, 2 caracteres mais curtos do que comprá-la manualmente.O segundo problema é que
i
sai em pontos fixos em vez de repetir para sempre, e se o padrão e a substituição forem iguais e você fizer uma substituição, isso se parecerá com um ponto fixo para J. Lidamos com isso inserindo um loop infinito de negar 1 sobre e se detectarmos que são iguais: esta é a-i@=`p@.~:~/
parte, substituindop&|.
.Esse ciclo se repete devido ao uso de
i
, até que algo fora dossub
erros ocorra. Até onde sei, isso só pode acontecer quando estamos sem personagens, ou quando jogamos fora um conjunto incompleto de padrões e substituições.Curiosidades sobre este golfe:
;:
é mais curto do que a iteração manual da string.0{
deve ter a chance de errar antes desub
entrar em um loop infinito; portanto, deve funcionar bem se o padrão corresponder à substituição, mas nunca aparecer no restante da fonte. No entanto, esse pode ou não ser um comportamento não especificado, pois não consigo encontrar uma citação nos documentos. Whoopsie.i
, esses erros também ficam presos. Dependendo de quando você pressiona Ctrl + C, você pode:sub
loop tentando concatenar um número para uma sequência e, em seguida, continue interpretando /// como se tivesse terminado de substituir uma sequência por si mesma um número infinito de vezes.sub
metade e continue interpretando uma expressão /// meio-subbedada.Exemplo de uso:
fonte
/\\/good/\/
caso de teste; a depuração informa que o problema é meu uso1!:2&4
, pois o jqt não possui stdin / out. Irá investigar. Quais são seus9!:12''
e9!:14''
?9!:12''
é 6 e9!:14''
é j701 / 2011-01-10 / 11: 25.Perl - 190
Lê o
///
programa de stdin até EOF.fonte
m/^(.*?)(?<!\\)\/(.*?)(?<!\\)\/(.*?)(?<!\\)\/(.*)$/s
--ie corresponderá à produção, ao padrão e à substituição de uma só vez - contribuirá para um golfe mais curto? Eu não conheço nenhum Perl, eu mesmo./a/\0/a
Pip ,
100102 bytesEu nunca tinha provado que Pip estava completo em Turing (embora seja obviamente óbvio) e, em vez de seguir a rota usual de BF, pensei / seria interessante. Depois de ter a solução, pensei em jogar e postar aqui.
101 bytes de código, +1 para
-r
sinalizador:Aqui está a minha versão não destruída, com muitos comentários:
Experimente online! (Observe que o TIO não fornece saída quando o programa não termina e também possui um limite de tempo. Para exemplos maiores e loops infinitos, é recomendável executar o Pip na linha de comando.)
fonte
pip + -r
, 101 bytesC ++: Visual C ++ 2013 = 423, g ++ 4.9.0 = 442
Isso nunca vai ganhar, mas desde que eu decidi que todos os meus futuros projetos de software serão escritos nessa linguagem incrível, eu precisava de um intérprete para isso e achei que poderia compartilhar o que fiz ...
A diferença na pontuação é que o Visual C ++ não precisa da primeira inclusão, mas o g ++ precisa. A pontuação assume que as terminações de linha contam como 1.
fonte
if(!o[i]);
comoif(P
para salvar caracteres, ou estou mal-entendido como #define funciona?P
inmain
possui um espaço a seguir, para que você possa salvar um personagem substituindo esses espaços por ponto e vírgula e removendo-o de#define
. Então, se você puder usar#define
s dentro de outros, poderá economizar um pouco mais reescrevendoN(x)
como em(92==P
vez deo[i]==92
e daO
mesma forma.N(x)
comoP;else if(n<x)(P==92?
e mudando chamadas paraN
conformidade poderia economizar alguns bytes.Python 2 (236), Python 3 (198?)
Chamado como
d(r"""/foo/Hello, world!//B\/\\R/foo/B/\R""")
. As aspas triplas são necessárias apenas se o///
programa contiver novas linhas: caso contrário, aspas simples estão ok.EDIT: Este intérprete agora imprime as coisas conforme o esperado (anteriormente, ele era impresso apenas no final, veja comentários). Para o Python 3, remova a primeira linha (mas não tenho o Python 3 na minha instalação antiga, portanto, não posso ter certeza de que não há outra alteração).
fonte
/a/ab/bbaa/abb
./a/ab/bbaa/abb
ficará preso em um loop sem fim, sem imprimir nada, porque a primeira substituição éa
=>ab
. O corretoa/ab/bbaa/abb
funciona como anunciado.-u
para forçar o buffer de saída a ser armazenado em buffer.Cobra - 226
fonte
Ruby ,
119110 bytesEncerra com exceção
Experimente online!
Termina de forma limpa (116 bytes)
Experimente online!
fonte
Python 2/3 (211 bytes)
O código a seguir, com base na resposta de Bruno Le Floch , é compatível com Python 2 e Python 3.
Além disso, por ser iterativo e não recursivo, não corre o risco de atingir a profundidade máxima de recursão do Python.
fonte
in(0,1,2)
parain 0,1,2
e[""]*2+[1]
para["","",1]
, resultando em 211 bytes .BaCon ,
391387395 bytesA partir das contribuições desta página, consegui apenas o programa Python para trabalhar. Os outros trabalham para algumas amostras ///, ou não funcionam. Portanto, decidi adicionar minha versão, que é uma implementação no BASIC.
Competir em um concurso CodeGolf com o BASIC não é fácil, pois o BASIC usa palavras longas como declarações. A única abreviação comumente encontrada no BASIC é o '?' sinal, o que significa PRINT.
Portanto, o programa abaixo pode nunca vencer, mas pelo menos funciona com todo o código de demonstração nesta página do Codegolf e no Wiki da Esolangs . Incluindo todas as versões das "99 garrafas de cerveja".
fonte