Esegui Stackylogic


45

Stackylogic è un linguaggio di programmazione basato sulla logica che ho inventato che accetta 0"s 1" e "s" per input e output un singolo 0o 1al completamento.

Un programma Stackylogic è composto da righe che possono contenere solo i tre caratteri 01?e esattamente uno <alla fine di una delle righe. Linee possono non essere vuoto e la riga con il <devono avere almeno una 0, 1o ?prima.

Ecco un programma di esempio che (come spiegherò) calcola la NAND di due bit:

1
?<
11
?
0

Ogni riga in un programma Stackylogic è considerata una pila , con il fondo a sinistra e quello in alto a destra. Implicitamente, c'è uno stack vuoto (riga vuota) prima della prima riga in un programma e dopo l'ultima riga.

Il <, che chiameremo cursore , segna l'inizio dello stack quando viene eseguito un programma Stackylogic. L'esecuzione di un programma Stackylogic procede come segue:

  1. Rimuovi il carattere superiore dallo stack a cui punta attualmente il cursore.

    • Se il personaggio è ?, chiedi all'utente a 0o a 1e agisci come se quello fosse il personaggio.
    • Se il personaggio è 0, sposta il cursore di una pila verso l'alto (sulla riga sopra la riga corrente).
    • Se il personaggio è 1, sposta il cursore di una pila verso il basso (sulla riga sotto la riga corrente).
  2. Se lo stack su cui si sposta il cursore è vuoto, emette l'ultimo valore estratto da uno stack (sempre a 0o 1) e termina il programma.

  3. Altrimenti, se lo stack su cui si sposta il cursore non è vuoto, tornare al passaggio 1 e ripetere il processo.

Si noti che i programmi Stackylogic finiscono sempre perché devono infine esaurire le loro pile.

Esempio NAND

Nel programma NAND il cursore inizia su un ?:

1
?<
11
?
0

Supponiamo che l'utente inserisca una 1volta che ?viene visualizzato, il che significa che il cursore si sposterà verso il basso, facendo apparire il programma così:

1

11<
?
0

Ora una pianura si 1trova nella parte superiore della pila di cursori. Viene opportunamente estratto e il cursore si sposta di nuovo:

1

1
?<
0

Ora supponiamo che gli input dell'utente 0per il ?, il che significa che il cursore si sposterà verso l'alto:

1

1<

0

Ancora una volta, a si 1trova sulla pila del cursore, quindi il cursore si apre e si sposta verso il basso:

1


<
0

Alla fine lo stack del cursore è vuoto, quindi 1viene emesso l'ultimo valore, il , viene emesso e il programma termina.

Questo è preciso per un gate NAND perché lo 1 NAND 0è 1. Questo ovviamente funziona per gli altri tre ingressi a due bit se si desidera verificare.

O Esempio

Questo programma Stackylogic simula un gate OR :

?
?<

È facile vedere che un input iniziale di 1spingerà il cursore nello stack vuoto implicito sotto l'ultima riga, terminando il programma e producendo quello 1che era appena input.

Per un input di, 00d'altra parte, il cursore si dirige verso lo stack vuoto implicito in alto, terminando il programma e producendo l'ultimo 0da inserire.

Sfida

Scrivi un programma o una funzione che accetta un programma Stackylogic come stringa e lo esegue, stampando o restituendo il risultante 0o 1.

Al ?'s, è possibile richiedere all'utente una 0o 1ingresso, o leggere il valore da una stringa di preset 0' s e 1's che si assumono come input. (Questo potrebbe essere un altro input di stringa per il tuo programma / funzione o potresti semplicemente supporre che la prima o l'ultima riga della stringa di programma sarà il flusso di input).

Si può presumere che il programma e l'input siano sempre ben formati. Si può facoltativamente supporre che i programmi di input vengano forniti con una nuova riga finale (sebbene si ricordi che alla fine c'è sempre uno stack vuoto implicito).

Vince il codice più breve in byte.

Altri programmi di esempio

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

Grazie Martin per i programmi mediani .


Se si desidera aggiungere una funzione a 3 ingressi, ecco un modo per implementare mediana: ?\1?<\??. In alternativa, ecco un'implementazione simmetrica a 5 righe:?\?0\?<\?1\?
Martin Ender

Oh, ho trovato un'implementazione più ordinato anche: 1\???<\0.
Martin Ender,

2
L'implementazione più accurata di @ MartinEnder della funzione mediana a 3 input (equivalentemente, la funzione delle regole di maggioranza) si generalizza bene. Ad esempio, la funzione delle regole di maggioranza a 7 input è 111\???????<\000.
Greg Martin,

Lascia che il "bizarro" di un programma Stackylogic $ P $ sia il programma $ BP $ formato invertendo l'ordine delle linee dei programmi originali e cambiando tutti gli 1 a 0 e viceversa (ma lasciando? Se solo <). Sembra che l'output di $ BP $ sugli input $ b_1, b_2, \ dots $ sia il NOT dell'output di $ P $ sugli input $! B_1,! B_2, \ dots $. Si noti che le implementazioni fornite di AND e OR sono correlate in modo bizzarro in questo modo, come lo sono NAND e NOR e le due versioni di XOR / XNOR. Alcuni programmi sono il loro bizarro (BUFFER, NOT, MEDIAN (v1)).
Greg Martin,

1
@GregMartin Yep. Credo che il termine tecnico sia dualità .
Calvin's Hobbies,

Risposte:


15

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

Il conteggio dei byte presuppone la codifica ISO 8859-1.

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

L'input avviene tramite STDIN e si prevede che sia l'input dell'utente separato da due avanzamenti di riga dal codice sorgente.

Provalo online! (Le prime due righe abilitano una suite di test, in cui ogni riga è un caso di test separato con /anziché linefeed.)

Non sono del tutto sicuro di cosa sia successo qui. Sembra una soluzione davvero ingombrante e questo non è davvero il tipo di problema per cui Retina è stato creato, ma batte ancora tutte le risposte attuali per qualche motivo.

Spiegazione

La versione finale di questo alla fine è stata abbastanza semplice.

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

Il primo stadio è semplicemente un ciclo (grazie +all'opzione) che esegue l'interpretazione effettiva della lingua. Lo stage è una singola sostituzione regex, ma in realtà sono in realtà tre diverse sostituzioni racchiuse in uno stage, sfruttando il fatto che catturare gruppi da rami inutilizzati viene semplicemente considerato vuoto durante la sostituzione.

  1. Elaborazione ?:

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

    Questo prende semplicemente il primo carattere dell'input, quindi abbina i caratteri arbitrari fino a quando non trova ?<, e mette quel primo carattere davanti al <(eliminando il ?).

  2. Elaborazione 0:

    (¶.*)0<
    <$1
    

    Corrisponde alla riga che precede a 0<e la inserisce dopo la <rimozione 0. (In effetti, questo elimina 0e sposta la <riga verso l'alto.)

  3. Elaborazione 1:

    1<(¶.+)
    $1<
    

    Praticamente la stessa cosa, tranne per il fatto che spostiamo di <una riga verso il basso durante l'eliminazione di a 1. Un dettaglio importante da notare è l'uso +invece di *, ovvero, è necessario che la riga successiva non sia vuota.

La parte interessante è capire perché questo funziona e perché non abbiamo bisogno di tenere traccia dell'ultimo valore che abbiamo spuntato per determinare l'output finale. Per fare ciò, dobbiamo considerare come può terminare il ciclo sopra. Poiché ogni possibile corrispondenza cambia la stringa (poiché almeno un carattere viene eliminato da essa), dobbiamo solo considerare i casi in cui la corrispondenza fallisce del tutto.

Se il personaggio di fronte <è ?l'unico modo per far fallire la partita è che non ci sono personaggi senza linefeed da nessuna parte di fronte ad esso, ma ciò non può accadere perché siamo garantiti che ci sia sempre abbastanza input.

Se il carattere davanti <è 0, il regex corrisponderà sempre, poiché c'è sempre un'altra riga sopra quella corrente (che potrebbe essere la riga vuota che separa l'input dal codice sorgente).

Se il personaggio davanti <è 1, la regex fallirà se siamo sull'ultima riga (poiché la corrispondenza non riuscirà) o se la riga successiva è vuota (poiché la .+corrispondenza non riuscirà). Si noti che entrambi questi casi corrispondono alla conclusione del programma dopo aver visualizzato a 1.

Infine, c'è anche la possibilità che <non sia preceduta da nessuno dei ?01. Si scopre che possiamo raggiungere questa situazione solo saltando su 0e spostandoci su una linea vuota, in modo che <ora sia preceduto da un avanzamento di riga.

Quindi, quando il programma termina con a 1, lo <sarà ancora dopo 1. Ma se il programma termina con a 0, si sposta su una riga vuota. Possiamo facilmente trasformare queste informazioni nell'output desiderato con una semplice fase di corrispondenza:

1<

Questo conta semplicemente le corrispondenze di 1<nella stringa. Con il ragionamento di cui sopra ciò avverrà 1se il programma è terminato con a 1e 0se è terminato con a 0.


3
Signore, siete un mago.
GamrCorps,

Tale Regex Mch Wow
Rohan Jhunjhunwala il

12

Convesso , 102 95 byte

Bene, un linguaggio basato su un elenco di stack codificato in un linguaggio basato su stack si è rivelato piuttosto difficile. Segna le mie parole: lo porterò a 100 byte o meno! EDIT: successo!

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

Provalo online!

L'input del programma avviene tramite arg dalla riga di comando. Input 0s e 1s normalmente (su TIO, questo significa newline-separati nella casella "input").


Spiegazione:

Tutto il codice può essere diviso in tre parti:

N/S\+

Questo bit prende semplicemente il programma di input e lo converte in un array di righe e aggiunge anche " "righe all'inizio dell'array. Poiché le matrici di Convex si avvolgono, l'avere solo una pila vuota all'inizio.

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

Questa parte determina con quale linea (o pila) iniziare l'esecuzione. Cerca attraverso ogni riga e inserisce il numero di stack corretto nella Mvariabile.

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

Questa è la parte divertente! Cicla continuamente fino a quando non raggiunge una linea con solo uno spazio ( " ") su di essa (che simboleggia una pila vuota). Se la riga non è vuota, procede come segue:

  1. Rimuovi l'ultimo personaggio dalla pila.
  2. Istruzione switch:
    1. Se il carattere è a ?, accetta input e aggiungi quel carattere alla riga.
    2. Se il personaggio è a 0, sposta il puntatore di linea verso l'alto di uno.
    3. Se il carattere è a 1, sposta il puntatore di linea verso il basso di uno.
    4. Se il personaggio è uno (spazio), stampa l'elemento estratto più di recente e termina il programma.

6

Codice macchina x86 a 32 bit, 70 byte

In esadecimale:

FC89E1565F31D28A07A8DF740B3C3C7511428D5C24FCEB0A5729142484C07405B20147EBE2578B3B8A17F6C2DF7414FF0B923C3F7501AC3C30750383C30883EB04EBE389CCC3

L'input è una stringa multilinea con terminazione NULL (separata da avanzamento riga) passata tramite ESI. Si presume che l'input dell'utente sia la prima riga. Restituisce '0' / '1' in AL.

Smontaggio:

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), 136 138

Supponendo una nuova riga finale nel programma

(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++]")

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

Test

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 byte

Salvato 2 byte grazie a Emigna ! Codice:

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

Utilizza la codifica CP-1252 . Provalo online! .


2

Python 3, 147 146 145 144 byte

1 byte grazie 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 è il programma, z viene immesso. Sì, i miei nomi delle variabili sono pazzi.


1

ES6, 190 byte

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
}

Usa come f(program, input)


2
Un paio di suggerimenti generali sul golf (c'è un elenco di questi da qualche parte): usa [...o]invece di o.split``e usa forinvece di while, in quanto ti consente di spostare due espressioni nel forsalvataggio di due byte. Un paio di suggerimenti specifici: penso che il tuo Numbercast non sia necessario, come *2farà il cast per te, e vorrei solo leggere iusando j=0e i[j++]che penso risparmi 11 byte.
Neil,

1
Non sono necessarie le f=funzioni anonime consentite.
gcampbell,

0

Java, 256 255 231 219 215 213 byte

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 su Ideone.

Prende il programma e immette come argomenti e restituisce il risultato come intero.


@LeakyNun Modificato in un forciclo, ma cosa significa il tuo primo commento?
PurkkaKoodari,

@ Pietu1998 LeakyNun significa che può essere int f(String[]I)...e puoi evitare ilString[]p=I.split("\n");
gatto

Significa che puoi dichiarare la funzione comeint f(String[]P)
Leaky Nun

1
@cat ninja'd di 7 secondi: /
Leaky Nun

Inoltre, se ti accontenti di Java 8 puoi avere un lambda come (penso)->(String[]I){...
cat

0

PHP (<7.0), 195 192 byte

Prende il programma come primo argomento e ogni valore come argomento aggiuntivo.
Nota che ho provato questo con split ("", ..) come spazi piuttosto che newline ma dovrebbe funzionare comunque.
Fornisce un avviso obsoleto se eseguito in php> 5.3.
Fornisce inoltre un avviso se si esce dalla parte superiore del programma. Tuttavia funziona ancora e genera correttamente quindi va bene.

<?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 non funziona molto bene con la manipolazione di stringhe, ma è piuttosto breve.

Funziona scansionando la stringa per il cursore ( <), spostandosi indietro di 1 posizione, leggendo il comando, sostituendolo con un tabcarattere e spostandosi avanti o indietro di una riga. L'input ha la forma di un array di caratteri C, come char array[]="1\n?<\n11\n?\n0";result = f(array);, sebbene anche i ritorni a capo siano consentiti.

Sebbene la stringa di input venga modificata, la lunghezza non viene modificata.

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

Programma di test

Esegui questo programma con ciascun caso di test come parametro separato, usando una singola barra rovesciata al posto delle nuove righe. I casi di test saranno separati da una riga vuota.

#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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.