Führen Sie Stackylogic aus


45

Stackylogic ist eine logikbasierte Programmiersprache, die ich erfunden habe 0und die Ein 1- und Ausgänge für eine einzelne 0oder 1nach Fertigstellung enthält.

Ein Stackylogic-Programm besteht aus Zeilen, die nur die drei Zeichen 01?sowie genau eines <am Ende einer der Zeilen enthalten können. Zeilen dürfen nicht leer sein und die Zeile mit dem <muss mindestens eins 0haben 1, oder ?davor.

Hier ist ein Beispielprogramm, das (wie ich erklären werde) das NAND von zwei Bits berechnet :

1
?<
11
?
0

Jede Zeile in einem Stackylogic-Programm wird als Stapel betrachtet , mit dem unteren Rand links und dem oberen Rand rechts. Implizit steht ein leerer Stapel (leere Zeile) vor der ersten Zeile in einem Programm und nach der letzten Zeile.

Das Symbol <, das wir als Cursor bezeichnen , markiert den Stack, auf dem gestartet werden soll, wenn ein Stackylogic-Programm ausgeführt wird. Die Ausführung eines Stackylogic-Programms erfolgt wie folgt:

  1. Entfernen Sie das oberste Zeichen vom Stapel, auf den der Cursor gerade zeigt.

    • Ist dies der Fall ?, fordern Sie den Benutzer zur Eingabe von a 0oder a auf, 1und tun Sie so, als wäre dies der Charakter.
    • Wenn das Zeichen ist 0, bewegen Sie den Cursor einen Stapel nach oben (bis zur Zeile über der aktuellen Zeile).
    • Wenn das Zeichen ist 1, bewegen Sie den Cursor einen Stapel nach unten (in die Zeile unter der aktuellen Zeile).
  2. Wenn der Stapel, auf den sich der Cursor bewegt, leer ist, geben Sie den letzten Wert aus, der von einem Stapel abgefallen ist (immer ein 0oder 1), und beenden Sie das Programm.

  3. Wenn der Stapel, auf den sich der Cursor bewegt, nicht leer ist, kehren Sie zu Schritt 1 zurück und wiederholen Sie den Vorgang.

Beachten Sie, dass Stackylogic-Programme immer enden, da sie ihre Stacks letztendlich entleeren müssen.

NAND-Beispiel

Im NAND-Programm startet der Cursor auf einem ?:

1
?<
11
?
0

Wir werden die Benutzereingaben ein nehmen , 1sobald die ?aufgetaucht ist, was bedeutet , dass der Cursor nach unten bewegen wird, so dass das Programm wie folgt aussehen:

1

11<
?
0

Jetzt befindet sich eine Ebene 1oben auf dem Cursorstapel. Es wird ordnungsgemäß geknallt und der Cursor bewegt sich erneut:

1

1
?<
0

Nehmen wir nun die Benutzereingaben 0für an ?, was bedeutet, dass sich der Cursor nach oben bewegt:

1

1<

0

Wieder 1befindet sich a auf dem Cursorstapel, sodass der Cursor springt und sich nach unten bewegt:

1


<
0

Schließlich ist der Cursor-Stapel leer, so dass der letzte Wert 1ausgegeben wird und das Programm endet.

Dies ist genau für ein NAND-Gatter, weil es 1 NAND 0ist 1. Dies funktioniert natürlich auch für die anderen drei Zwei-Bit-Eingänge, wenn Sie dies überprüfen möchten.

ODER Beispiel

Dieses Stackylogic-Programm simuliert ein ODER- Gatter:

?
?<

Es ist leicht zu erkennen, dass bei der ersten Eingabe von 1der Cursor auf den impliziten leeren Stapel unterhalb der letzten Zeile bewegt wird, wodurch das Programm beendet und der 1gerade eingegebene ausgegeben wird.

Bei einer Eingabe von 00hingegen bewegt sich der Cursor zum impliziten leeren Stapel oben, beendet das Programm und gibt den zuletzt 0eingegebenen aus.

Herausforderung

Schreiben Sie ein Programm oder eine Funktion, die ein Stackylogic-Programm als Zeichenfolge aufnimmt und es ausführt und das resultierende 0oder ausgibt oder zurückgibt 1.

Bei ?können Sie den Benutzer zur Eingabe von a 0oder 1auffordern oder den Wert aus einer voreingestellten Zeichenfolge von 0s und 1s lesen, die Sie auch als Eingabe verwenden. (Dies kann eine weitere Zeichenfolge sein, die in Ihr Programm / Ihre Funktion eingegeben wird, oder Sie können einfach davon ausgehen, dass die erste oder letzte Zeile der Programmzeichenfolge der Eingabestream ist.)

Sie können davon ausgehen, dass das Programm und die Eingabe immer gut geformt sind. Sie können optional davon ausgehen, dass Eingabeprogramme mit einer einzelnen nachgestellten Zeile versehen sind (denken Sie jedoch daran, dass am Ende immer ein impliziter leerer Stapel steht).

Der kürzeste Code in Bytes gewinnt.

Weitere Beispielprogramme

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

Dank Martin für die Median - Programme .


Wenn Sie eine 3-Eingabefunktion hinzufügen möchten, hier ist ein Weg , Median zu implementieren: ?\1?<\??. Alternativ ist hier eine symmetrische 5-Zeilen-Implementierung:?\?0\?<\?1\?
Martin Ender

Oh, ich fand eine noch sauberere Umsetzung: 1\???<\0.
Martin Ender

2
@ MartinEnders übersichtlichere Implementierung der 3-Eingangs-Median-Funktion (entsprechend der Mehrheitsregeln-Funktion) lässt sich gut verallgemeinern. Zum Beispiel ist die 7-Eingabe-Mehrheitsregeln-Funktion 111\???????<\000.
Greg Martin

Das "Bizarre" eines Stackylogic-Programms $ P $ sei das Programm $ BP $, das durch Umkehren der Zeilenreihenfolge der ursprünglichen Programme und Ändern aller Einsen in Nullen und umgekehrt (aber ohne? S und <allein) gebildet wird. Es scheint, dass die Ausgabe von $ BP $ an den Eingängen $ b_1, b_2, \ dots $ NICHT die Ausgabe von $ P $ an den Eingängen $! B_1,! B_2, \ dots $ ist. Beachten Sie, dass die angegebenen Implementierungen von AND und OR auf diese Weise bizarr sind, ebenso wie NAND und NOR und die beiden Versionen von XOR / XNOR. Einige Programme sind bizarr (BUFFER, NOT, MEDIAN (v1)).
Greg Martin

1
@ GregMartin Yep. Ich glaube, der Fachbegriff ist Dualität .
Calvins Hobbys

Antworten:


15

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

Die Anzahl der Bytes setzt die Kodierung nach ISO 8859-1 voraus.

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

Die Eingabe erfolgt über STDIN und wird als Benutzereingabe erwartet, die durch zwei Zeilenvorschübe vom Quellcode getrennt ist.

Probieren Sie es online! (Die ersten beiden Zeilen ermöglichen eine Testsuite, bei der jede Zeile ein separater Testfall mit /anstelle von Zeilenvorschüben ist.)

Ich bin nicht ganz sicher, was hier passiert ist. Das fühlt sich nach einer wirklich klobigen Lösung an und das ist wirklich nicht das Problem, für das Retina gemacht wurde, aber es übertrifft aus irgendeinem Grund immer noch alle aktuellen Antworten.

Erläuterung

Die endgültige Version davon war eigentlich ziemlich einfach.

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

Die erste Stufe ist einfach eine Schleife (aufgrund der +Option), die das eigentliche Interpretieren der Sprache durchführt. Die Stufe ist eine einzelne Regex-Substitution, aber tatsächlich sind es drei verschiedene Substitutionen, die in einer Stufe zusammengefasst sind, indem die Tatsache ausgenutzt wird, dass das Erfassen von Gruppen aus nicht verwendeten Zweigen während der Substitution einfach als leer betrachtet wird.

  1. Bearbeitung ?:

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

    Dies nimmt einfach das erste Zeichen der Eingabe, stimmt dann mit beliebigen Zeichen überein, bis es gefunden wird ?<, und setzt das erste Zeichen vor das <(Löschen des ?).

  2. Bearbeitung 0:

    (¶.*)0<
    <$1
    

    Dies entspricht der Zeile vor a 0<und setzt es nach dem <Entfernen 0. (Tatsächlich löscht dies nur das 0und verschiebt die <eine Zeile nach oben.)

  3. Bearbeitung 1:

    1<(¶.+)
    $1<
    

    Ziemlich dasselbe, außer dass wir <beim Löschen von a eine Zeile nach unten gehen 1. Ein wichtiges Detail, das zu beachten ist, ist die Verwendung von +anstelle von *, das heißt, wir verlangen, dass die nächste Zeile nicht leer ist.

Der interessante Teil ist, herauszufinden, warum dies funktioniert und warum wir nicht den letzten Wert verfolgen müssen, den wir abgerufen haben, um die endgültige Ausgabe zu bestimmen. Dazu müssen wir uns überlegen, wie die obige Schleife enden kann. Da bei jeder möglichen Übereinstimmung die Zeichenfolge geändert wird (da mindestens ein Zeichen aus der Zeichenfolge entfernt wird), müssen nur Fälle berücksichtigt werden, in denen die Übereinstimmung insgesamt fehlschlägt.

Wenn das Zeichen vor <ist ?der einzige Weg für das Spiel zum Scheitern verurteilt ist , dass es nicht nicht-Zeilenvorschub - Zeichen überall vor ihm ist, aber das kann nicht geschehen , weil wir garantiert sind , dass immer genügend Eingang ist.

Wenn das Zeichen vor " <is" steht 0, stimmt der reguläre Ausdruck immer überein, da sich immer eine weitere Zeile über der aktuellen befindet (dies kann die leere Zeile sein, die die Eingabe vom Quellcode trennt).

Wenn das Zeichen vor " <ist" ist 1, schlägt der reguläre Ausdruck fehl, wenn wir uns entweder in der letzten Zeile befinden (da die Zeichen nicht übereinstimmen) oder wenn die nächste Zeile leer ist (da die .+Zeichen nicht übereinstimmen). Beachten Sie, dass in beiden Fällen das Programm nach dem Aufrufen von a beendet wird 1.

Schließlich gibt es auch die Möglichkeit, dass <von keiner vorangeht ?01. Es hat sich herausgestellt, dass wir diese Situation nur erreichen können, indem wir 0auf eine leere Zeile aufspringen , sodass der Zeile <jetzt ein Zeilenvorschub vorausgeht.

Also, wenn das Programm auf einem beendet wird 1, <wird das immer noch danach sein 1. Wenn das Programm jedoch mit einem beendet wird 0, ist es in eine leere Zeile verschoben worden. Mit einer einfachen Match-Phase können wir diese Informationen leicht in die gewünschte Ausgabe umwandeln:

1<

Dies zählt einfach die Übereinstimmungen 1<in der Zeichenfolge. Nach der obigen Überlegung wird dies der Fall sein, 1wenn das Programm auf einem beendet 1wird und 0wenn es auf einem beendet wird 0.


3
Sie, Sir, sind ein Zauberer.
GamrCorps

Such Regex Mch Wow
Rohan Jhunjhunwala

12

Konvex , 102 bis 95 Bytes

Nun, eine Liste von stapelbasierten Sprachen, die in einer stapelbasierten Sprache codiert sind, stellte sich als ziemlich schwierig heraus. Markiere meine Worte: Ich werde das auf 100 Bytes oder weniger bringen! EDIT: Erfolg!

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

Probieren Sie es online!

Die Programmeingabe erfolgt über Befehlszeilenargumente. Geben Sie 0s und 1s normalerweise ein (bei TIO bedeutet dies Zeilenumbruch im Feld "Eingabe").


Erläuterung:

Der gesamte Code kann in drei Teile aufgeteilt werden:

N/S\+

Dieses Bit nimmt einfach das Eingabeprogramm und konvertiert es in ein Array von Zeilen und fügt außerdem " "Zeilen am Anfang des Arrays hinzu. Da die Arrays von Convex umgebrochen werden, reicht es aus, am Anfang nur einen leeren Stapel zu haben.

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

Dieser Teil bestimmt, mit welcher Zeile (oder mit welchem ​​Stapel) die Ausführung gestartet werden soll. Es durchsucht jede Zeile und gibt die richtige Stack-Nummer in die MVariable ein.

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

Das ist das lustige Stück! Es wird so lange wiederholt, bis es eine Zeile mit nur einem Leerzeichen ( " ") erreicht (Symbol für einen leeren Stapel). Wenn die Zeile nicht leer ist, geschieht Folgendes:

  1. Ziehe das letzte Zeichen vom Stapel.
  2. Switch-Anweisung:
    1. Wenn das Zeichen a ist ?, nehmen Sie die Eingabe vor und hängen Sie das Zeichen an die Zeile an.
    2. Wenn das Zeichen a ist 0, bewegen Sie den Zeilenzeiger um eins nach oben.
    3. Wenn das Zeichen a ist 1, bewegen Sie den Zeilenzeiger um eins nach unten.
    4. Wenn das Zeichen ein (Leerzeichen) ist, drucken Sie das zuletzt angezeigte Element und beenden Sie das Programm.

6

32-Bit-x86-Computercode, 70 Byte

In hex:

FC89E1565F31D28A07A8DF740B3C3C7511428D5C24FCEB0A5729142484C07405B20147EBE2578B3B8A17F6C2DF7414FF0B923C3F7501AC3C30750383C30883EB04EBE389CCC3

Die Eingabe ist eine NULL-terminierte mehrzeilige Zeichenfolge (durch Zeilenvorschub getrennt), die über ESI übergeben wird. Es wird angenommen, dass die Benutzereingabe die erste Zeile ist. Gibt '0' / '1' in AL zurück.

Demontage:

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

Annehmen eines abschließenden Zeilenumbruchs im Programm

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

Weniger golfen

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

Prüfung

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>



2

Python 3, 147 146 145 144 Bytes

1 Byte danke an @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 ist das Programm, z wird eingegeben. Ja, meine Variablennamen sind verrückt.


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
}

Verwenden Sie wie f(program, input)


2
Ein paar allgemeine Golftipps (es gibt irgendwo eine Liste davon): Verwenden Sie [...o]anstelle von o.split``und verwenden Sie foranstelle von while, damit Sie zwei Ausdrücke in die forsparenden zwei Bytes verschieben können. Ein paar konkrete Tipps: Ich denke, Ihre NumberBesetzung ist unnötig, da die *2Besetzung für Sie bestimmt ist, und ich würde einfach lesen, iindem ich j=0und i[j++]was ich denke, spart 11 Bytes.
Neil

1
Du brauchst die nicht f=, anonyme Funktionen sind erlaubt.
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 auf Ideone.

Nimmt das Programm und die Eingabe als Argumente und gibt das Ergebnis als Ganzzahl zurück.


@LeakyNun In eine forSchleife geändert , aber was bedeutet Ihr erster Kommentar?
PurkkaKoodari

@ Pietu1998 LeakyNun heißt, es kann sein int f(String[]I)...und du kannst derString[]p=I.split("\n");
Katze aus dem

Dies bedeutet, dass Sie die Funktion alsint f(String[]P)
Leaky Nun

1
@cat Ninja'd von 7 Sekunden: /
Leaky Nun

Wenn Sie sich für Java 8 entscheiden, können Sie auch ein Lambda wie (glaube ich)->(String[]I){...
cat

0

PHP (<7,0), 195 192 Bytes

Nimmt das Programm als erstes Argument und jeden Wert als zusätzliches Argument.
Beachten Sie, dass ich dies mit split ("", ..) als Leerzeichen und nicht mit Zeilenumbrüchen getestet habe, aber es sollte trotzdem funktionieren.
Gibt einen veralteten Hinweis, wenn in PHP> 5.3 ausgeführt.
Gibt auch eine Warnung aus, wenn Sie das Programm verlassen. Es funktioniert jedoch immer noch und wird korrekt ausgegeben, sodass es in Ordnung ist.

<?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 kann nicht so gut mit Strings umgehen, aber das ist ziemlich kurz.

Sie scannt die Zeichenfolge nach dem Cursor ( <), bewegt sich 1 Stelle zurück, liest den Befehl, ersetzt ihn durch ein tabZeichen und bewegt sich entweder eine Zeile vorwärts oder rückwärts. Die Eingabe erfolgt in Form eines C char-Arrays char array[]="1\n?<\n11\n?\n0";result = f(array);, obwohl auch Wagenrückläufe zulässig sind.

Obwohl die Eingabezeichenfolge geändert wird, wird die Länge nicht geändert.

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

Testprogramm

Führen Sie dieses Programm mit jedem Testfall als separaten Parameter aus, wobei Sie anstelle von Zeilenumbrüchen einen einzelnen Backslash verwenden. Testfälle werden durch eine Leerzeile getrennt.

#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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.