Executar Stackylogic


45

Stackylogic é uma linguagem de programação baseada em lógica que inventei 0's 1' e 's para entrada e saída de um único 0ou 1após a conclusão.

Um programa Stackylogic consiste em linhas que podem conter apenas os três caracteres 01?, bem como exatamente um <no final de uma das linhas. As linhas podem não estar vazio e a linha com o <deve ter pelo menos um 0, 1ou ?antes dela.

Aqui está um exemplo de programa que (como explicarei) calcula a NAND de dois bits:

1
?<
11
?
0

Cada linha em um programa Stackylogic é considerada uma pilha , com a parte inferior à esquerda e a parte superior à direita. Implicitamente, há uma pilha vazia (linha vazia) antes da primeira linha de um programa e depois da última linha.

O <, que chamaremos de cursor , marca a pilha para iniciar quando um programa Stackylogic é executado. A execução de um programa Stackylogic ocorre da seguinte maneira:

  1. Retire o caractere superior da pilha para a qual o cursor está apontando.

    • Se o personagem for ?, solicite a 0ou a um usuário 1e aja como se fosse o personagem.
    • Se o caractere for 0, mova o cursor uma pilha para cima (para a linha acima da linha atual).
    • Se o caractere for 1, mova o cursor uma pilha para baixo (para a linha abaixo da linha atual).
  2. Se a pilha para a qual o cursor se move estiver vazia, imprima o último valor que foi retirado de uma pilha (sempre a 0ou 1) e encerre o programa.

  3. Caso contrário, se a pilha para a qual o cursor se move não estiver vazia, volte para a etapa 1 e repita o processo.

Observe que os programas Stackylogic sempre terminam porque eles acabam esgotando suas pilhas.

Exemplo NAND

No programa NAND, o cursor inicia em ?:

1
?<
11
?
0

Assumiremos que o usuário digita uma 1vez que ?é acionado, o que significa que o cursor se moverá para baixo, fazendo com que o programa fique assim:

1

11<
?
0

Agora, uma planície 1está no topo da pilha do cursor. É devidamente acionado e o cursor se move novamente:

1

1
?<
0

Agora assuma as entradas do usuário 0para o ?, o que significa que o cursor se moverá para cima:

1

1<

0

Novamente, a 1está na pilha de cursores, então o cursor aparece e se move para baixo:

1


<
0

Finalmente, a pilha do cursor está vazia; portanto, o último valor exibido, o 1, é gerado e o programa termina.

Isso é preciso para uma porta NAND porque 1 NAND 0é 1. Obviamente, isso funciona para as outras três entradas de dois bits, se você quiser verificar.

Exemplo OR

Este programa Stackylogic simula uma porta OR :

?
?<

É fácil ver que uma entrada inicial de 1empurrará o cursor para a pilha vazia implícita abaixo da última linha, encerrando o programa e emitindo a 1que acabou de ser inserida.

Por 00outro lado, para uma entrada de , o cursor seguirá para a pilha vazia implícita na parte superior, encerrando o programa e emitindo a última 0a ser inserida.

Desafio

Escreva um programa ou função que pegue um programa Stackylogic como uma string e execute-o, imprimindo ou retornando o 0ou resultante 1.

Após a ?'s, você pode solicitar que o usuário para uma 0ou 1entrada, ou ler o valor de uma string pré-estabelecido de 0' s e 1's que você também tomar como entrada. (Essa pode ser outra entrada de string para seu programa / função ou você pode simplesmente assumir que a primeira ou a última linha da string de programa será o fluxo de entrada).

Você pode assumir que o programa e a entrada estão sempre bem formados. Opcionalmente, você pode assumir que os programas de entrada vêm com uma única nova linha à direita (embora lembre-se de que sempre há uma pilha vazia implícita no final).

O código mais curto em bytes vence.

Mais programas de amostra

ZERO
0<

ONE
1<

BUFFER
?<

NOT
1
?<
0

AND
?<
?

NAND
1
?<
11
?
0

OR
?
?<

NOR
1
?
00
?<
0

XOR(v1)
?
0
1?<
?
0

XOR(v2)
?
?<
11
?
0

XNOR(v1)
1
?
0?<
1
?

XNOR(v2)
1
?
00
?<
?

MEDIAN(v1)
1
???<
0

MEDIAN(v2)
?
1?<
??

Obrigado Martin pelos programas medianos .


Se você quiser adicionar uma função de 3-entradas, aqui está uma maneira de implementar mediana: ?\1?<\??. Como alternativa, aqui está uma implementação simétrica de 5 linhas:?\?0\?<\?1\?
Martin Ender

Oh, eu encontrei uma implementação mais puro mesmo: 1\???<\0.
Martin Ender

2
A implementação mais organizada do @ MartinEnder da função mediana de 3 entradas (equivalentemente, a função de regras da maioria) generaliza bem. Por exemplo, a função de regras principais de 7 entradas é 111\???????<\000.
Greg Martin

Deixe o "bizarro" de um programa Stackylogic $ P $ ser o programa $ BP $ formado invertendo a ordem das linhas dos programas originais e alterando todos os 1s para 0s e vice-verso (mas deixando? S e <sozinho). Parece que a saída de $ BP $ nas entradas $ b_1, b_2, \ dots $ é NÃO da saída de $ P $ nas entradas $! B_1,! B_2, \ dots $. Observe que as implementações fornecidas de AND e OR são relacionadas a bizarros dessa maneira, assim como NAND e NOR e as duas versões do XOR / XNOR. Alguns programas são bizarros (BUFFER, NOT, MEDIAN (v1)).
Greg Martin

1
@GregMartin Yep. Eu acredito que o termo técnico é dualidade .
9788 Calvin's Hobbies

Respostas:


15

Retina , 79 78 73 68 66 65 63 62 55 44 bytes

A contagem de bytes assume a codificação ISO 8859-1.

+`(.)([^_]*)\?<|(¶.*)0<|1<(¶.+)
$2$1$4<$3
1<

A entrada é via STDIN e espera-se que seja a entrada do usuário separada por dois feeds de linha do código-fonte.

Experimente online! (As duas primeiras linhas ativam um conjunto de testes, em que cada linha é um caso de teste separado, em /vez de feeds de linha.)

Não tenho muita certeza do que aconteceu aqui. Parece uma solução realmente desajeitada e esse não é realmente o tipo de problema para o qual a Retina foi criada, mas ainda supera todas as respostas atuais por algum motivo.

Explicação

A versão final disso acabou sendo bastante simples.

+`(.)([^_]*)\?<|(¶.*)0<|1<(¶.+)
$2$1$4<$3

O primeiro estágio é simplesmente um loop (devido à +opção) que faz a interpretação real do idioma. O estágio é uma substituição de regex única, mas na verdade são três substituições diferentes compactadas em um estágio, aproveitando o fato de que grupos de captura de ramificações não utilizadas são simplesmente considerados vazios durante a substituição.

  1. Processamento ?:

    (.)([^_]*)\?<
    $2$1<
    

    Isso simplesmente pega o primeiro caractere da entrada, depois combina caracteres arbitrários até encontrar ?<, e coloca esse primeiro caractere na frente de <(excluindo o ?).

  2. Processamento 0:

    (¶.*)0<
    <$1
    

    Isso corresponde à linha que precede a 0<e a coloca após a <remoção 0. (Efetivamente, isso apenas exclui 0e move a <linha para cima.)

  3. Processamento 1:

    1<(¶.+)
    $1<
    

    Praticamente a mesma coisa, exceto que movemos <uma linha para baixo ao excluir a 1. Um detalhe importante a ser observado é o uso de, em +vez de *, ou seja, exigir que a próxima linha não fique vazia.

A parte interessante é descobrir por que isso funciona e por que não precisamos acompanhar o último valor que exibimos para determinar a saída final. Para fazer isso, precisamos considerar como o loop acima pode terminar. Como todas as correspondências possíveis alteram a sequência (já que pelo menos um caractere é descartado), precisamos considerar apenas os casos em que a correspondência falha completamente.

Se o caractere na frente <é ?a única maneira de a partida falhar é que não há caractere sem avanço de linha em nenhum lugar à sua frente, mas isso não pode acontecer porque estamos garantidos de que sempre há entrada suficiente.

Se o caractere na frente de <for 0, o regex sempre corresponderá, pois sempre há outra linha acima da atual (que pode ser a linha vazia que separa a entrada do código-fonte).

Se o caractere na frente de <for 1, a regex falhará se estivermos na última linha (pois a correspondência falhará) ou se a próxima linha estiver vazia (pois a .+correspondência falhará). Observe que os dois casos correspondem ao encerramento do programa após a exibição de a 1.

Finalmente, há também a possibilidade que <não é precedida por nenhum ?01. Acontece que só podemos chegar a essa situação pulando um 0e movendo-o para uma linha vazia, de modo que <agora seja precedido por um avanço de linha.

Assim, quando o programa termina em um 1, o <continuará sendo depois disso 1. Mas se o programa terminar em um 0, ele será movido para uma linha vazia. Podemos facilmente transformar essas informações na saída desejada com um estágio de correspondência simples:

1<

Isso simplesmente conta as correspondências de 1<na string. Pelo raciocínio acima, isso ocorrerá 1se o programa tiver terminado em a 1e 0se tiver terminado em a 0.


3
Você senhor, é um mago.
GamrCorps

Como Regex Mch Wow
Rohan Jhunjhunwala

12

Convexo , 102 95 bytes

Bem, uma linguagem baseada em lista de pilhas codificada em uma linguagem baseada em pilha acabou sendo bastante difícil. Marque minhas palavras: chegarei a 100 bytes ou menos! EDIT: Sucesso!

N/S\+{s)_'<={R:M;}{R):R;+}?}%'<-M){(æ=)s_:Q;"?10 ""l+ M):M; M(:M; W:M;A~p"S/Ë~~_!S*+tM)Q:A;}h;;

Experimente online!

A entrada do programa é via args da linha de comando. Entrada 0s e 1s normalmente (na TIO, este meio de nova linha-separado na caixa de "entrada").


Explicação:

Todo o código pode ser dividido em três partes:

N/S\+

Este bit simplesmente pega o programa de entrada e o converte em uma matriz de linhas e também adiciona " "linhas ao início da matriz. Uma vez que as matrizes do Convex são finalizadas, ter apenas uma pilha vazia no início serve.

{s)_'<={R:M;}{R):R;+}?}%'<-

Esta parte determina com qual linha (ou pilha) iniciar a execução. Ele pesquisa em cada linha e coloca o número de pilha correto na Mvariável.

M){(æ=)s_:Q;"?10 ""l+ M):M; M(:M; W:M;A~p"S/Ë~~_!S*+tM)Q:A;}h;;

Esta é a parte divertida! Ele faz um loop contínuo até atingir uma linha com apenas um espaço ( " ") (simbolizando uma pilha vazia). Se a linha não estiver vazia, faça o seguinte:

  1. Retire o último caractere da pilha.
  2. Alternar declaração:
    1. Se o caractere for a ?, insira e inclua esse caractere na linha.
    2. Se o caractere for a 0, mova o ponteiro de linha para cima um.
    3. Se o caractere for a 1, mova o ponteiro de linha para baixo um.
    4. Se o caractere for um (espaço), imprima o item exibido mais recentemente e finalize o programa.

6

Código de máquina x86 de 32 bits, 70 bytes

Em hexadecimal:

FC89E1565F31D28A07A8DF740B3C3C7511428D5C24FCEB0A5729142484C07405B20147EBE2578B3B8A17F6C2DF7414FF0B923C3F7501AC3C30750383C30883EB04EBE389CCC3

Input é uma sequência de várias linhas terminada em NULL (separada por avanço de linha) transmitida via ESI. A entrada do usuário é assumida como a primeira linha. Retorna '0' / '1' em AL.

Desmontagem:

fc           cld
89 e1        mov    ecx,esp
56           push   esi
5f           pop    edi                  ;EDI=ESI
31 d2        xor    edx,edx              ;EDX=0
_loop0:
8a 07        mov    al,BYTE PTR [edi]    ;AL=*EDI
a8 df        test   al,0xf5              ;AL&~0x0a==0 => separator ('\n' or '\0')
74 0b        je     _stck
3c 3c        cmp    al,'<'
75 11        jne    _loop0end
42           inc    edx                  ;For "cursor" symbol adjust stack pointer offset
8d 5c 24 fc  lea    ebx,[esp-0x4]        ;and load EBX with the address where this pointer
eb 0a        jmp    _loop0end            ;is going to be stored in the next iteration
_stck:
57           push   edi                  ;Pointer to the separator
29 14 24     sub    DWORD PTR [esp],edx  ;adjusted to point to the top of the stack
84 c0        test   al,al                ;AL==0?
74 05        je     _loop0break          ;break
b2 01        mov    dl,0x1               ;EDX can be [0..2], resets to 1
_loop0end:
47           inc    edi                  ;++EDI
eb e2        jmp    _loop0
_loop0break:
57           push   edi                  ;*EDI==0, add lower implicit empty stack
_loop1:                                  ;The actual state machine code
8b 3b        mov    edi,DWORD PTR [ebx]  ;EDI=*EBX
8a 17        mov    dl,BYTE PTR [edi]    ;DL=*EDI
f6 c2 df     test   dl,0xf5              ;DL&~0x0a
74 14        je     _loop1break          ;ZF==1 => current stack is empty
ff 0b        dec    DWORD PTR [ebx]      ;--(*EBX): pop the stack
92           xchg   edx,eax              ;AL=DL
3c 3f        cmp    al,'?'
75 01        jne    _skplods             ;AL=='?' => substitute char from the input string
ac           lodsb
_skplods:
3c 30        cmp    al,'0'
75 03        jne    _0x31                ;EBX+=AL==0?4:-4
83 c3 08     add    ebx,0x8              ;But to avoid wasting 2 bytes for the jump after the 'add'
_0x31:                                   ;add 8 and fall through to subtract 4 back
83 eb 04     sub    ebx,0x4
eb e3        jmp    _loop1
_loop1break:
89 cc        mov    esp,ecx              ;Clear the stack
c3           ret                         ;Returns '0'/'1' in AL

5

JavaScript (ES6), 136138

Assumindo uma nova linha final no programa

(p,i,j=0)=>eval("for(p=`\n${p}`.split`\n`.map((x,i)=>((c=(x=[...x]).pop())=='<'?k=i:x.push(c),x));a=p[k].pop();k-=1-c-c)c=1/a?a:i[j++]")

Menos golfe

(p, i, j=0)=>{
  p=`\n${p}`
     .split`\n`
     .map(
       (x,i)=>
       (
         x = [...x],
         c = x.pop(),
         c == '<' ? k=i : x.push(c),
         x
       )
     )
  for(; a = p[k].pop(); k -= 1-c-c)
    c = 1/a ? a : i[j++];
  return c;
}

Teste

F=(p,i,j=0)=>eval("for(p=`\n${p}`.split`\n`.map((x,i)=>((c=(x=[...x]).pop())=='<'?k=i:x.push(c),x));a=p[k].pop();k-=1-c-c)c=1/a?a:i[j++]")

function run() {
  var pgm=P.value+'\n'
  var i=I.value
  O.textContent = F(pgm,i)
}

run()
#P { width:60%; height: 6em }
#I { width:50%;  }
Program<br>
<textarea id=P>1
?&lt;
11
?
0</textarea><br>
Input<br>
<input id=I value=01>
<button onclick='run()'>Run</button>
<br>Output
<pre id=O></pre>


5

05AB1E , 58 56 55 53 51 50 46 bytes

Economizou 2 bytes graças a Emigna ! Código:

õ|`õ)[¤'<å#À]`¨[¬V¨Y'?QiI«ëYi1U)À`ëY_i0U)Á`ëXq

Usa a codificação CP-1252 . Experimente online! .


2

Python 3, 147 146 145 145 144 bytes

1 byte graças a @Lynn.

def f(p):
 i=p[:p.find("<")].count("\n");p=p.split()
 try:
  while 1:*p[i],c=p[i];c=c>"<"and input()or c;i+=c<"<"and int(c)*2-1
 except:return c

1

Python 3, 318

def s(f,z):
 p=b="";g=0;a=[];n=a.append;n(p)
 for i in f:
  if i=="\n":n(p);p=''
  else:p+=i
 n(p);p=b;n(p)
 while g<len(a):
  if'<'in a[g]:q=g;a[q]=a[q][:-1]
  g+=1
 while 1:
  v=a[q]
  if v=='':print(b);break
  if v[-1]=='1':a[q]=v[:-1];q+=1;b=1
  elif v[-1]=="0":a[q]=v[:-1];q-=1;b=0
  else:a[q]=v[:-1]+z[0];z=z[1:]

F sendo o programa, z sendo inserido. Sim, meus nomes de variáveis ​​são loucos.


1

ES6, 190 bytes

f=(p,i)=>{
n=p.split`<`[0].split`\n`.length-1
p=p.split`\n`.map(o=>o.split``)
i=i.split``
p[n].pop()
while(p[n]&&p[n].length){
c=p[n].pop()
v=c=='?'?i.shift():Number(c)
n+=v*2-1
}
return v
}

Use como f(program, input)


2
Algumas dicas gerais sobre golfe (há uma lista delas em algum lugar): use em [...o]vez de o.split``e use em forvez de while, pois isso permite mover duas expressões para forsalvar dois bytes. Algumas dicas específicas: eu acho que o seu Numberelenco é desnecessário, pois o *2elenco será para você, e eu apenas leria iusando j=0e i[j++]que acho que economiza 11 bytes.
Neil

1
Você não precisa das f=funções anônimas.
9788 Julio

0

Java, 256 255 231 219 215 213 bytes

int f(char[][]p,char[]I){int l=p.length,d=0,j=-1,c=0,k=0,i[]=new int[l];while(++j<l)if(p[j][i[j]=p[j].length-1]==60)i[k=j]--;try{for(;;k+=c>48?1:-1)c=(c=p[k][i[k]--])>49?I[d++]:c;}catch(Throwable t){}return c-48;}

Demonstração sobre Ideone.

Pega o programa e a entrada como argumentos e retorna o resultado como um número inteiro.


@LeakyNun Alterou para um forloop, mas o que seu primeiro comentário significa?
PurkkaKoodari

@ Pietu1998 LeakyNun significa que pode ser int f(String[]I)...e você pode evitar oString[]p=I.split("\n");
cat

Isso significa que você pode declarar a função comoint f(String[]P)
Leaky Nun

1
@cat ninja'd por 7 segundos: /
Leaky Nun

Além disso, se você se contentar com Java 8 você pode ter um lambda como (eu acho)->(String[]I){...
gato

0

PHP (<7.0), 195 192 bytes

Leva o programa como primeiro argumento e cada valor como um argumento adicional.
Observe que eu testei isso com espaços asn divididos ("", ..) em vez de novas linhas, mas deve funcionar de qualquer maneira.
Dá um aviso obsoleto se executado em php> 5.3.
Também dá um aviso se você sair do topo do programa. No entanto, ele ainda funciona e sai corretamente, então tudo bem.

<?php foreach(split("\n",$argv[++$t])as$l)$p[]=str_split($l);for($i=-1;end($p[++$i])!='<';);array_pop($p[$i]);for(;($v=array_pop($p[$i]))!==null;$i+=$n?:-1)($n=$v)=='?'&&$n=$argv[++$t];echo$n;

0

C, 264 249 244 242

C não se sai tão bem com a manipulação de strings, mas isso é bastante curto.

Ele funciona pesquisando a seqüência de caracteres em busca do cursor ( <), recuando 1 lugar, lendo o comando, substituindo-o por um tabcaractere e avançando ou recuando uma linha. A entrada está na forma de uma matriz de caracteres C, como char array[]="1\n?<\n11\n?\n0";result = f(array);, embora os retornos de carro também sejam permitidos.

Embora a sequência de entrada seja modificada, o comprimento não é alterado.

t;f(char*n){char*p=strchr(n,60);for(*p--=9;;){if(*p==63)scanf("%d",&t),*p=t+48;if(*p^49){for(*p--=9;p>n&&*p^10;--p);for(--p;p>n&&*p==9;--p);if(p<n||*p==10)return 0;}else{for(*p++=9;*p&&*p^10;++p);for(p+=!!*p;*p>10;++p);if(*--p<11)return 1;}}}

Programa de teste

Execute este programa com cada caso de teste como um parâmetro separado, usando uma única barra invertida no lugar de novas linhas. Os casos de teste serão separados por uma linha em branco.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, const char **argv)
{
    while (*++argv)
    {
        char *input=malloc(strlen(*argv)+1),*p;
        strcpy(input,*argv);
        printf("testing %s\n",input);
        for (p=input;*p;++p)
            if (*p=='\\')
                *p=10;
        printf("result: %d\n\n",f(input));
        free(input);
    }
    return 0;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.