Ejecute Stackylogic

45

Stackylogic es un lenguaje de programación basado en la lógica me hice a la que tienen en 0's y 1' s para la entrada y salida de una única 0o 1al finalizar.

Un programa Stackylogic consta de líneas que solo pueden contener los tres caracteres 01?, así como exactamente uno <al final de una de las líneas. Las líneas pueden no estar vacío y la línea con el <deben tener al menos una 0, 1o ?antes de ella.

Aquí hay un programa de muestra que (como explicaré) calcula la NAND de dos bits:

1
?<
11
?
0

Cada línea en un programa Stackylogic se considera una pila , con la parte inferior a la izquierda y la parte superior a la derecha. Implícitamente, hay una pila vacía (línea vacía) antes de la primera línea de un programa y después de la última línea.

El <, que llamaremos el cursor , marca la pila para comenzar cuando se ejecuta un programa Stackylogic. La ejecución de un programa Stackylogic procede de la siguiente manera:

  1. Saque el carácter superior de la pila a la que apunta actualmente el cursor.

    • Si el personaje es ?, solicite al usuario a 0o a 1y actúe como si ese fuera el personaje.
    • Si el personaje es 0, mueva el cursor una pila hacia arriba (a la línea sobre la línea actual).
    • Si el personaje es 1, mueva el cursor una pila hacia abajo (a la línea debajo de la línea actual).
  2. Si la pila a la que se mueve el cursor está vacía, muestre el último valor que se extrajo de una pila (siempre una 0o 1) y finalice el programa.

  3. De lo contrario, si la pila a la que se mueve el cursor no está vacía, regrese al paso 1 y repita el proceso.

Tenga en cuenta que los programas de Stackylogic siempre terminan porque eventualmente deben agotar sus pilas.

Ejemplo NAND

En el programa NAND, el cursor comienza en ?:

1
?<
11
?
0

Asumiremos que el usuario ingresa una 1vez que ?aparece, lo que significa que el cursor se moverá hacia abajo, haciendo que el programa se vea así:

1

11<
?
0

Ahora hay un plano 1en la parte superior de la pila del cursor. Está debidamente reventado y el cursor se mueve de nuevo:

1

1
?<
0

Ahora suponga que el usuario ingresa 0para el ?, lo que significa que el cursor se moverá hacia arriba:

1

1<

0

Nuevamente, a 1está en la pila del cursor, de modo que el cursor aparece y se mueve hacia abajo:

1


<
0

Finalmente, la pila del cursor está vacía, por lo que aparece el último valor 1, se emite y finaliza el programa.

Esto es exacto para una compuerta NAND porque lo 1 NAND 0es 1. Por supuesto, esto funciona para las otras tres entradas de dos bits si desea verificar.

O ejemplo

Este programa Stackylogic simula una puerta OR :

?
?<

Es fácil ver que una entrada inicial de 1empujará el cursor a la pila vacía implícita debajo de la última línea, finalizando el programa y generando el 1que solo se ingresó.

Por 00otro lado, para una entrada de , el cursor se abrirá camino hacia la pila vacía implícita en la parte superior, finalizando el programa y generando la última 0entrada.

Desafío

Escriba un programa o función que tome un programa Stackylogic como una cadena y lo ejecute, imprimiendo o devolviendo el resultado 0o 1.

Luego de ?'s, puede solicitar al usuario una entrada 0o 1, o leer el valor de una cadena preestablecida de 0' sy 1'que también toma como entrada. (Esto podría ser otra entrada de cadena a su programa / función o simplemente podría asumir que la primera o última línea de la cadena de programa será la secuencia de entrada).

Puede suponer que el programa y la entrada siempre están bien formados. Opcionalmente, puede suponer que los programas de entrada vienen con una nueva línea final (aunque recuerde que siempre hay una pila vacía implícita al final).

El código más corto en bytes gana.

Más programas de muestra

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?<
??

Gracias Martin por los programas medianos .

Pasatiempos de Calvin
fuente
Si desea agregar una función de 3 entradas, aquí está una manera de implementar mediana: ?\1?<\??. Alternativamente, aquí hay una implementación simétrica de 5 líneas:?\?0\?<\?1\?
Martin Ender
Oh, me encontré con una implementación más ordenado, incluso: 1\???<\0.
Martin Ender
2
La implementación más ordenada de @ MartinEnder de la función mediana de 3 entradas (equivalentemente, la función de reglas de mayoría) se generaliza muy bien. Por ejemplo, la función de reglas de mayoría de 7 entradas es 111\???????<\000.
Greg Martin
Deje que el "bizarro" de un programa Stackylogic $ P $ sea el programa $ BP $ formado invirtiendo el orden de las líneas de los programas originales y cambiando todos los 1s a 0s y viceversa (pero dejando? Sy <solo). Parece que la salida de $ BP $ en las entradas $ b_1, b_2, \ dots $ es el NO de la salida de $ P $ en las entradas $! B_1,! B_2, \ dots $. Observe que las implementaciones dadas de AND y OR están relacionadas de manera extraña de esta manera, como lo son NAND y NOR, y las dos versiones de XOR / XNOR. Algunos programas son su propio bizarro (BUFFER, NOT, MEDIAN (v1)).
Greg Martin
1
@GregMartin Sí. Creo que el término técnico es dualidad .
Hobbies de Calvin

Respuestas:

15

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

El recuento de bytes asume la codificación ISO 8859-1.

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

La entrada es a través de STDIN y se espera que sea la entrada del usuario separada por dos avances de línea del código fuente.

Pruébalo en línea! (Las primeras dos líneas habilitan un conjunto de pruebas, donde cada línea es un caso de prueba separado en /lugar de avances de línea).

No estoy completamente seguro de lo que sucedió aquí. Esto se siente como una solución realmente torpe y este no es realmente el tipo de problema para el que se hizo Retina, pero aún supera todas las respuestas actuales por alguna razón.

Explicación

La versión final de esto en realidad terminó siendo bastante simple.

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

La primera etapa es simplemente un bucle (debido a la +opción) que hace la interpretación real del lenguaje. La etapa es una sola sustitución de expresiones regulares, pero en realidad son tres sustituciones diferentes comprimidas en una sola etapa, al hacer uso del hecho de que capturar grupos de ramas no utilizadas simplemente se considera vacío durante la sustitución.

  1. Procesamiento ?:

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

    Esto simplemente toma el primer carácter de la entrada, luego coincide con los caracteres arbitrarios hasta que encuentra ?<, y pone ese primer carácter delante de <(eliminando ?).

  2. Procesamiento 0:

    (¶.*)0<
    <$1
    

    Esto coincide con la línea que precede a a 0<y la coloca después de la <eliminación 0. (Efectivamente, esto simplemente elimina 0y mueve la <línea hacia arriba).

  3. Procesamiento 1:

    1<(¶.+)
    $1<
    

    Más o menos lo mismo, excepto que movemos <una línea hacia abajo mientras eliminamos a 1. Un detalle importante a tener en cuenta es el uso de en +lugar de *, es decir, requerimos que la siguiente línea no esté vacía.

La parte interesante es descubrir por qué esto funciona y por qué no necesitamos hacer un seguimiento del último valor que obtuvimos para determinar el resultado final. Para hacer eso, debemos considerar cómo puede terminar el ciclo anterior. Dado que cada posible coincidencia cambia la cadena (ya que al menos se elimina un carácter de ella), solo necesitamos considerar los casos en que la coincidencia falla por completo.

Si el carácter que está delante <es ?la única forma de que falle la coincidencia es que no hay ningún personaje sin salto de línea en ningún lugar delante de él, pero eso no puede suceder porque estamos garantizados de que siempre hay suficiente entrada.

Si el carácter delante de <es 0, la expresión regular siempre coincidirá, ya que siempre hay otra línea por encima de la actual (que puede ser la línea vacía que separa la entrada del código fuente).

Si el carácter delante de <es 1, la expresión regular fallará si estamos en la última línea (ya que no coincidirá) o si la siguiente línea está vacía (ya .+que no coincidirá). Tenga en cuenta que ambos casos corresponden a la finalización del programa después de hacer estallar a 1.

Finalmente, también existe la posibilidad de que <no esté precedido por ninguno de ?01. Resulta que solo podemos llegar a esta situación haciendo estallar 0ay subiendo a una línea vacía, de modo que <ahora está precedido por un salto de línea.

Entonces, cuando el programa termina en a 1, el <seguirá siendo después de eso 1. Pero si el programa termina en a 0, se habrá movido a una línea vacía. Podemos convertir fácilmente esta información en la salida deseada con una simple etapa de coincidencia:

1<

Esto simplemente cuenta las coincidencias de 1<en la cadena. Según el razonamiento anterior, esto será 1si el programa terminó en a 1, y 0si terminó en a 0.

Martin Ender
fuente
3
Usted señor, es un mago.
GamrCorps
Tales Regex Mch Wow
Rohan Jhunjhunwala
12

Convexo , 102 95 bytes

Bueno, un lenguaje basado en una lista de pilas codificado en un lenguaje basado en pilas resultó ser bastante difícil. Marque mis palabras: ¡Llegaré a 100 bytes o menos! EDITAR: ¡Éxito!

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;;

Pruébalo en línea!

La entrada del programa es a través de argumentos de línea de comando. De entrada 0s y 1s normalmente (en TIO, esto significa salto de línea separados en el cuadro de "entrada").


Explicación:

Todo el código se puede dividir en tres partes:

N/S\+

Este bit simplemente toma el programa de entrada y lo convierte en una matriz de líneas, y también agrega " "líneas al comienzo de la matriz. Dado que las matrices convexas se ajustan, bastará con tener una pila vacía al principio.

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

Esta parte determina con qué línea (o pila) comenzar la ejecución. Busca a través de cada línea y pone el número de pila correcto en la Mvariable.

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

Esta es la parte divertida! Continuamente se repite hasta que llega a una línea con solo un espacio ( " ") (que simboliza una pila vacía). Si la línea no está vacía, hace lo siguiente:

  1. Saca al último personaje de la pila.
  2. Declaración de cambio:
    1. Si el carácter es a ?, ingrese y agregue ese carácter a la línea.
    2. Si el carácter es un 0, mueva el puntero de línea hacia arriba uno.
    3. Si el carácter es un 1, mueva el puntero de línea hacia abajo uno.
    4. Si el carácter es un (espacio), imprima el elemento emergente más reciente y finalice el programa.
GamrCorps
fuente
6

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

En hexadecimal:

FC89E1565F31D28A07A8DF740B3C3C7511428D5C24FCEB0A5729142484C07405B20147EBE2578B3B8A17F6C2DF7414FF0B923C3F7501AC3C30750383C30883EB04EBE389CCC3

La entrada es una cadena de varias líneas terminada en NULL (separada por salto de línea) pasada a través de ESI. Se supone que la entrada del usuario es la primera línea. Devuelve '0' / '1' en AL.

Desmontaje

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
meden
fuente
5

JavaScript (ES6), 136138

Asumiendo una nueva línea de terminación en el 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 golf

(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;
}

Prueba

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>

edc65
fuente
5

05AB1E , 58 56 55 53 51 50 46 bytes

¡ Ahorré 2 bytes gracias a Emigna ! Código:

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

Utiliza la codificación CP-1252 . Pruébalo en línea! .

Adnan
fuente
2

Python 3, 147 146 145 144 bytes

1 byte gracias 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
PurkkaKoodari
fuente
1

Pitón 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 es el programa, z es la entrada. Sí, mis nombres de variables son una locura.

Limón Destructible
fuente
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
}

Usar como f(program, input)

Solo ASCII
fuente
2
Un par de consejos generales de golf (hay una lista de estos en alguna parte): use en [...o]lugar de o.split``y use en forlugar de while, ya que eso le permite mover dos expresiones a los fordos bytes que se guardan. Un par de consejos específicos: creo que su Numbertransmisión es innecesaria, ya que la *2enviará para usted, y simplemente leería iusando j=0y i[j++]que creo ahorra 11 bytes.
Neil
1
No necesita f=, las funciones anónimas están permitidas.
gcampbell
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;}

Demo en Ideone.

Toma el programa y la entrada como argumentos y devuelve el resultado como un entero.

PurkkaKoodari
fuente
@LeakyNun Cambió a un forbucle, pero ¿qué significa su primer comentario?
PurkkaKoodari
@ Pietu1998 LeakyNun significa que puede ser int f(String[]I)...y puede evitar el String[]p=I.split("\n");
gato
Significa que puede declarar la función comoint f(String[]P)
Leaky Nun
1
@cat ninja'd por 7 segundos: /
Leaky Nun
Además, si te conformas con Java 8, puedes tener un lambda como (creo)->(String[]I){...
gato
0

PHP (<7.0), 195 192 bytes

Toma el programa como primer argumento y cada valor como argumento adicional.
Tenga en cuenta que probé esto con espacios divididos ("", ..) asn en lugar de líneas nuevas, pero debería funcionar de todos modos.
Da un aviso obsoleto si se ejecuta en php> 5.3.
También da una advertencia si sale de la parte superior del programa. Sin embargo, todavía funciona y sale correctamente, así que está bien.

<?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;
usuario55641
fuente
0

C, 264 249 244 242

A C no le va tan bien manipulando cadenas, pero esto es bastante corto.

Funciona escaneando la cadena para el cursor ( <), retrocediendo 1 lugar, leyendo el comando, reemplazándolo con un tabcarácter y moviendo una línea hacia adelante o hacia atrás. La entrada tiene la forma de una matriz de caracteres C, como char array[]="1\n?<\n11\n?\n0";result = f(array);, aunque también se permiten retornos de carro.

Aunque la cadena de entrada se modifica, la longitud no se modifica.

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 prueba

Ejecute este programa con cada caso de prueba como un parámetro separado, utilizando una barra invertida única en lugar de líneas nuevas. Los casos de prueba estarán separados por una línea en blanco.

#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;
}
owacoder
fuente