EDIT: Como alguns de vocês suspeitavam, houve um erro no intérprete oficial: a ordem da composição .
foi invertida. Eu tinha duas versões do intérprete e usei a errada aqui. Os exemplos também foram escritos para esta versão incorreta. Corrigi o intérprete no repositório e os exemplos abaixo. A descrição de >
também era um pouco ambígua, então eu corrigi isso. Além disso, desculpas por isso demorar tanto, eu fui pego em algumas coisas da vida real.
EDIT2: Meu intérprete teve um erro cuja implementação .
foi refletida nos exemplos (eles contavam com um comportamento indefinido). O problema está resolvido.
Introdução
Shift é uma linguagem de programação funcional esotérica que criei há alguns anos, mas publiquei hoje. É baseado em pilha, mas também possui currying automático como Haskell.
Especificação
Existem dois tipos de dados no Shift:
- Funções, que possuem uma aridade positiva arbitrária (número de entradas) e que retornam uma lista de saídas. Por exemplo, uma função que duplica sua única entrada possui aridade 1 e uma função que troca suas duas entradas tem aridade 2.
- Espaços em branco, todos idênticos e não têm outro objetivo além de não serem funções.
Um programa Shift consiste em zero ou mais comandos , cada um com um único caractere ASCII. Existem 8 comandos no total:
!
( aplicar ) exibe uma funçãof
e um valorx
da pilha e aplicaf
- se ax
. Sef
tiver arity 1, a listaf(x)
será adicionada à frente da pilha. Se houver aridaden > 1
, uma nova(n-1)
função -aryg
é enviada para a pilha. É preciso entradas e retornos .x1,x2,...,xn-1
f(x,x1,x2,...,xn-1)
?
(em branco ) coloca um espaço em branco na pilha.+
( clone ) envia para a pilha uma função unária que duplica sua entrada: qualquer valorx
é mapeado para[x,x]
.>
( shift ) empurra para a pilha uma função unária que assume uman
função -aryf
e retorna uma(n+1)
função -aryg
que ignora seu primeiro argumentox
, chamaf
os restantes e se alinhax
à frente do resultado. Por exemplo,shift(clone)
é uma função binária que recebe entradasa,b
e retornos[a,b,b]
./
( fork ) empurra para a pilha uma função ternária que aceita três entradasa,b,c
e retorna[b]
sea
estiver em branco ou[c]
não.$
( Chamada ) empurra para a pilha uma função binária que aparece uma funçãof
e um valorx
, e aplica-sef
ax
exatamente como!
faz..
( cadeia ) empurra para a pilha uma função binária que exibe duas funçõesf
eg
retorna sua composição: uma funçãoh
que tem a mesma aridadef
e que recebe suas entradas normalmente, aplicaf
- se a elas e depois aplica - se totalmenteg
ao resultado (chamadas tantas vezes quanto dita a sua aridade), com itens não utilizados da saída dof
restante no resultado deh
. Por exemplo, suponha quef
seja uma função binária que clone seu segundo argumento eg
seja chamada . Se a pilha contém[f,g,a,b,c]
e nós o fazemos.!!
, então ela contém[chain(f,g),a,b,c]
; se fizermos a!!
seguir,f
é aplicado primeiroa,b
, produzindo[a,b,b]
, entãog
é aplicado aos dois primeiros elementos disso, já que sua aridade é 2, produzindo[a(b),b]
, e a pilha finalmente será[a(b),b,c]
.@
( digamos ) empurra uma função unária que simplesmente retorna sua entrada e imprime0
se estivesse em branco e1
se fosse uma função.
Observe que todos os comandos, exceto !
simplesmente enviam um valor para a pilha, não há como executar entradas e a única maneira de produzir qualquer coisa é usar @
. Um programa é interpretado avaliando os comandos um por um, imprimindo 0
s ou 1
s sempre que "say" é chamado e saindo. Qualquer comportamento não descrito aqui (aplicar um espaço em branco, aplicar uma pilha de comprimento 0 ou 1, chamar "cadeia" em um espaço em branco etc.) é indefinido: o intérprete pode travar, falhar silenciosamente, solicitar informações ou qualquer outra coisa.
A tarefa
Sua tarefa é escrever um intérprete para o Shift. Deve levar do STDIN, da linha de comando ou do argumento da função um programa Shift a ser interpretado e imprimir em STDOUT ou retornar a saída resultante (possivelmente infinita) de 0
s e 1
s. Se você escreve uma função, deve poder acessar as saídas de tamanho infinito de alguma forma (gerador em Python, lista lenta em Haskell, etc). Como alternativa, você pode pegar outra entrada, um número n
e retornar pelo menos n
caracteres da saída, se for maior que n
.
A menor contagem de bytes vence e as brechas padrão não são permitidas.
Casos de teste
Este programa Shift imprime 01
:
?@!@@!
Começando pela esquerda: pressione um espaço em branco, pressione dizer e aplique a palavra no espaço em branco. Isso gera 0
. Em seguida, pressione say duas vezes e aplique a segunda palavra à primeira. Isso gera 1
.
Este programa faz um loop para sempre, não produzindo saída:
$+.!!+!!
Empurre a chamada e o clone e aplique a cadeia a eles (precisamos de dois !
s, pois a cadeia é uma função binária). Agora a pilha contém uma função que pega um argumento, o duplica e chama a primeira cópia no segundo. Com +!!
, duplicamos essa função e a chamamos por si mesma.
Este programa imprime 0010
:
?@$.++>!.!!.!!.!!!!+?/!!!@!@>!!!
Empurre um espaço em branco e diga . Em seguida, componha uma função binária que copie seu segundo argumento b
, copie o primeiro a
e o componha consigo mesmo, depois aplique a composição à cópia de b
, retornando [a(a(b)),b]
. Aplique para dizer e em branco, depois diga para os dois elementos restantes na pilha.
Este programa é impresso 0
. Para cada um !!!
que você anexa, ele imprime um adicional 0
.
?@+$>!>!+>!///!!>!>!.!!.!!.!!+!!!!
Empurre um espaço em branco e diga . Em seguida, componha uma função ternária que tome f,g,x
como entradas e retornos [f,f,g,g(x)]
. Clone essa função e aplique-a a si mesma, digamos , e ao espaço em branco. Este aplicativo não altera a pilha, para que possamos aplicar a função novamente quantas vezes quisermos.
Este programa imprime a sequência infinita 001011011101111...
, onde o número de 1
s sempre aumenta em um:
@?/!@>!??/!!>!+.!!.!!.!!.+>!.!!$$$$+$>!>!$>!>!+>!$>!>!>!+>!>!///!!>!>!>!.!!.!!.!!.!!.!!.!!.!!.!!.!!.!!+!!!!!
O repositório contém uma versão anotada.
fonte
f(x1, x2, ..., xn)
eg(y1, y2, ..., ym)
. A chamada.
aparece nos dois e pressiona uma funçãoh(z1, z2, ..., zn)
. Agora você pode consumir todos esses argumentos gradualmente fazendo curry com ele!
. Apósn
essas aplicações, a função restante tinha apenas um argumento e, nesse ponto, calculaf(z1, z2, ..., zn)
(ou seja,f
aplica-se a todos os argumentos que você inseriu), o que gera alguns novos valores e, em seguida, consome imediatamente osm
valores da pilha e os chamag
..
funciona exatamente como Martin descreveu, exceto que, sef
retornar uma lista menor quem
valores, o resultado será indefinido (a composição tem aridaden
, portanto, não pode ser consumido mais argumentos da pilha). Essencialmente, a saída def
é usada como uma pilha temporária, na qualg
são pressionados e aplicados osm
tempos de utilização!
, e o resultado disso é adicionado à pilha principal.Respostas:
Python 2,
752667534506445436427404398393 bytesIsso não é nada curto ... mas eu fiz o meu melhor. Qualquer sugestão de golfe seria muito apreciada ...
EDIT6: Agora é um script em vez de uma função. Salve-o em um arquivo (shift.py, forex) e execute-o com
$ python shift.py '<my_input>'
. Coloque a entrada entre aspas simples, ou o bash ficará louco com os caracteres de entrada.EDIT7: Aaaaaaand ... não é mais legível. Mas me livrei de mais 23 bytes, então isso é bom, eu acho? Vou postar uma versão não-destruída também.
EDIT8: Mais um golfe, graças a @Zgarb.
EDIT: obrigado a @DLosc pela ajuda no golfe! Conseguiu reduzi-lo em 85 bytes.
EDIT2: corte uma tonelada de invólucros desnecessários e solte-o em outros 133 bytes!
EDIT3: ... e mais 28 graças a @ Sp3000 e @orlp no chat!
EDIT4: com a ajuda do @orlp & @ Sp3000, removeu todos os decoradores e agora é 61 bytes mais curto.
EDIT5: ajuda meeeeee, eu não consigo parar de jogar isso .... mais 9 bytes se foram. Livrar-se da declaração de impressão final salvaria outros 7, mas se você executar m () em um loop, toda a saída estará na mesma linha ... tudo bem?
Aqui está uma versão não destruída:
A idéia básica é que a lista python funcione muito bem como uma pilha e, ao armazenar
u=k.append
, não apenas salvo caracteres,mas também posso usar(não mais!).@u
como decorador para empurrar funçõesComo as duas funções que atuam nas funções da n-arity precisam aceitar um número arbitrário de argumentos, eu tive que usar
*args
, o que significava que meu plano original de rastrear f.func_code.co_argcount precisava ser substituído por uma arity atributodecorador.Em termos de manipulação de programas infinitos, o intérprete é executado até atingir a profundidade recursiva máxima; o manipulador RuntimeError na parte inferior faz com que saia silenciosamente nesse ponto e imprime a sequência de saída atual.
Casos de teste:
fonte
['1','0'][...]
com apenas'10'[...]
. 3) Por quex is 0
e nãox==0
(oux<1
)? 4) Não se preocupe em especificarRuntimeError
, apenasexcept
fará. 5) Como você está usando o Python 2, as guias e os espaços contam como diferentes níveis de recuo - facilmente, mas devem economizar ~ 25 bytes.x.a==1and x(y)or u(a(x.a-1)(b.partial(x,y)))
- os operadores lógicos ainda estão em curto-circuito, mas usam menos caracteres que o ternário. Salve outro byte usandox.a-1
como condição (0 / false sex
for 1, diferente de zero / verdade de outra forma) e trocando o 'depois' e 'else' expressões:x.a-1and u(a(x.a-1)(b.partial(x,y)))or x(y)
. (Tenho a minha golf um pouco mais agora que você me ... passou; ^))x.a==1
é verdade, masx(y)
retorna algo falso, ele tenta avaliaru(...)
também. Mas parece que você não precisa salvar os míseros 3 bytes que lhe dariam! Eu concordo, senhor: você me superou.RuntimeError
enquanto o meu apenas pede ao usuário que redirecione o stderr ... então provavelmente estamos até discutindo. ; ^)*_
servem as lambdas?Ghostscript
Ainda não joguei golfe, pois ainda preciso trabalhar a função de análise.
Esta implementação usa
_
e em:
vez de>
e/
, e requer que todos os caracteres do programa sejam separados por espaços. Isto porque>
e/
não são nomes válidos em Postscript, e os operadores não são auto-delimitação, mas isso será corrigido quando eu escrever o analisador.A primeira parte do código deve ser bastante transparente, pois está apenas repetindo as definições das funções do operador. A mágica acontece na definição de
!
.O caminho
!
obras é simples: Em primeiro lugar, ele adiciona argumentox
paraf
prefixandox
ao conteúdo def
, empurrando-o de volta na pilha, e nomear uma cópia do resultadofun
.Em seguida, agrupa toda a pilha como uma matriz.
.runandhide
é uma extensão do Ghostscript para executar código em área restrita, ocultando o conteúdo da matriz anterior do procedimento em que é chamado. odict
comando envia um novo dicionário à pilha de dict, restringindo o escopo dos nomes definidos atéend
ele retorne. Ele também substitui=only
(o operador de saída que eu uso@
) por um fictício, suprimindo a saída durante a execução do teste.stopped
é o equivalente PostScript datry
instrução encontrada em outros idiomas e retorna true se seu procedimento gerou um erro e false se foi executado até a conclusão.Após a
fun
conclusão da execução de avaliação , o programa restaura a pilha original da matriz oculta e, sefun
concluída sem erros, a executa de verdade, mantendo a saída.fonte
Python3,
685670634633 bytesTenho certeza de que esta é a coisa mais longa que já joguei. Costumava ser um pouco legível, mas seguir o conselho de @ sirpercival eliminou essa desvantagem!
k
é a pilha, que contém funções representadas como seqüências de caracteres"h(0,0)"
(que é c h ain ). Quando uma função é passada como um argumento para outra função, torna-serepr
'd e todos os números incrementado:"h('h(1,1)',0)"
. Depois que todos os0
s são substituídos em uma função, a coisa toda é passada paraeval
, chamando a função Python apropriada - a maioria das quais são funções lambda geradas a partir da grande string na linha 6 pelaexec
linha 7.Obter vários níveis de funções aninhadas incrementadas, citadas e escapadas adequadamente foi a maior dor de cabeça. Eu poderia economizar um pouco mais em operações regex se pudesse assumir que o aninhamento de funções não avançaria mais que 9 níveis, mas, como apontado nos comentários, isso provavelmente não é uma suposição segura.
Versão anterior ungolfed do código:
A única falha em potencial desta implementação é que ela usa recursão; portanto, programas que deveriam ser infinitos atingem a profundidade máxima da recursão rapidamente. (Você provavelmente deseja redirecionar o stderr quando executar um programa infinito - caso contrário, o rastreamento da pilha irá inundar a saída real.) Fora isso, tudo parece estar funcionando.
fonte
lambda a
ek.pop()
.k.pop()
situação um pouco menos repetitivo, de qualquer maneira.)Ceilão,
116710571031Eu não entendo tão curto quanto as versões python mono-tipadas ...
import ceylon.language.meta.model{N=Function}import ceylon.collection{H=HashMap}interface D of F|b{}object b satisfies D{}class F(shared Integer a,[D+](D+)f,[D*]c=[])satisfies D{shared[D+]o(D i){[D+]s=[i].prepend(c);return a==1then f(*s)else[F(a-1,f,s)];}shared[D+]y([D+]i){return f(*i.prepend(c));}}F m<A>(N<[D+],A>f)given A satisfies[D+]=>F(f.parameterTypes.size,(D+i)=>f.apply(*i));[D,D]e(D x)=>[x,x];[F]t(F f){[D+]g(D+i){assert(is[D+]r=i.rest);return[i[0],*f.y(r)];}return[F(f.a+1,g)];}[D]k(D a,D d,D c)=>a==b then[d]else[c];[D+]l(F a,D x)=>a.o(x);[F]n(F f,F g){[D+]h(D+i){[D+]r=f.y(i);assert(is[D+]d=r[0:g.a]);return g.y(d).append(r[g.a...]);}return[F(f.a,h)];}[D]y(D x){process.write(x==b then"0"else"1");return[x];}class I(){variable D[]s=[];value c=H{'?'->b,'+'->m(`e`),'>'->m(`t`),'/'->m(`k`),'$'->m(`l`),'.'->m(`n`),'@'->m(`y`)};shared void r(Character i){if(i=='!'){assert(is F f=s[0],is D x=s[1]);s=f.o(x).append(s[2...]);}else{assert(is D d=c[i]);s=[d].append(s);}}}shared void z(){process.readLine()?.collect(I().r);}
Aqui está uma versão formatada (e comentada) do mesmo código (com espaços / novas linhas / comentários, torna-se 4867 bytes):
As funções clonar
e
, shiftt
, bifurcark
, chamarl
, dizery
e encadearn
usam a última letra dos nomes para a versão abreviada, porque isso causou menos colisões. (Curiosidades: fork foi originalmente definida da seguinte maneira:[Data] fork(Data a, Data b, Data c) => a == blank then [b] else [c];
- quando eu renomeeiblank
parab
, isso quebrou, porque agora comparava os parâmetrosa
eb
, em veza
com o branco Levei algum tempo para depuração.).o
z
função é compartilhada porque meu IDE executa essas funções - a ferramenta de linha de comando também pode executar as não compartilhadas.fonte