Виконати Стакілогік


45

Stackylogic - це мова програмування, заснована на логіці, яку я склав, що приймає 0's і 1' s для введення та виведення одного 0чи 1після завершення.

Програма Stackylogic складається з рядків, які можуть містити лише три символи 01?, а також точно один <в кінці одного з рядків. Лінії не можуть бути порожніми і лінія з <повинна мати принаймні один 0, 1або ?перед ним.

Ось зразок програми, яка (як я поясню) обчислює NAND з двох біт:

1
?<
11
?
0

Кожен рядок у програмі Stackylogic вважається стеком , внизу зліва та вгорі праворуч. Безпосередньо перед першим рядком програми та після останнього рядка є порожній стек (порожній рядок).

Команда <, яку ми будемо називати курсором , позначає стек, який повинен запускатися при запуску програми Stackylogic. Виконання програми Stackylogic відбувається наступним чином:

  1. Спустіть верхній символ із стека, на який вказує курсор.

    • Якщо символ є ?, запропонуйте користувачеві a 0або a 1і діяти так, як ніби це був символ.
    • Якщо символ є 0, перемістіть курсор на один стек вгору (до рядка над поточним рядком).
    • Якщо символ є 1, перемістіть курсор на одну стек вниз (до рядка нижче поточного рядка).
  2. Якщо стек, на який рухається курсор, порожній, виведіть останнє значення, яке вискочило з стека (завжди a 0або 1), і закінчіть програму.

  3. В іншому випадку, якщо стек, на який рухається курсор, не порожній, поверніться до кроку 1 і повторіть процес.

Зауважте, що програми Stackylogic завжди закінчуються, оскільки вони з часом повинні вичерпати свій набір.

Приклад NAND

У програмі NAND курсор запускається на ?:

1
?<
11
?
0

Ми будемо вважати, що користувач вводить один 1раз, коли ?спливає, що означає, що курсор буде рухатися вниз, завдяки чому програма виглядає так:

1

11<
?
0

Тепер 1у верхній частині стеку курсора рівнина . Він належним чином спливає, і курсор рухається знову:

1

1
?<
0

Тепер припустимо, що користувальницькі введення 0для ?, що означає, що курсор переміститься вгору:

1

1<

0

Знову ж, a 1стоїть на стеці курсору, тому курсор спливає і рухається вниз:

1


<
0

Нарешті стек курсорів порожній, тому 1виводиться останнє значення , виводиться, і програма закінчується.

Це точно для воріт NAND, оскільки 1 NAND 0є 1. Це, звичайно, працює для інших трьох двобітних входів, якщо ви хочете перевірити.

АБО Приклад

Ця програма Stackylogic імітує ворота АБО :

?
?<

Неважко помітити, що початковий вхід 1воріт підштовхне курсор до неявного порожнього стека нижче останнього рядка, закінчивши програму та вивівши те, 1що було тільки введенням.

Для введення з 00іншого боку, курсор пробивається до неявного порожнього стека вгорі, закінчуючи програму і виводячи останній 0для введення.

Виклик

Напишіть програму або функцію, яка приймає в програмі Stackylogic як рядок і запускає її, друкуючи або повертаючи отримані 0або 1.

Після ?'s, ви можете запропонувати користувачеві ввести 0або 1ввести або прочитати значення з попередньо заданих рядків 0' s і 1', які ви також приймаєте як вхідні дані. (Це може бути ще один рядковий вхід до вашої програми / функції, або ви можете просто припустити, що перший або останній рядок рядка програми буде вхідним потоком).

Ви можете припустити, що програма і введення завжди добре сформовані. Можливо, ви можете припустити, що програми введення поставляються з одним зворотним новим рядком (хоча пам’ятайте, що в кінці завжди є неявний порожній стек).

Виграє найкоротший код у байтах.

Більше зразкових програм

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

Дякую Мартіну за середні програми .


Якщо ви хочете додати функцію в 3-вході, ось один з способів реалізації медіани: ?\1?<\??. Як варіант, ось симетрична 5-рядова реалізація:?\?0\?<\?1\?
Мартін Ендер

О, я знайшов навіть реалізацію акуратніше: 1\???<\0.
Мартін Ендер

2
@ MartinEnder акуратніше реалізацію медіанної функції з 3 входами (що еквівалентно функції більшості правил) добре узагальнено. Наприклад, функція правил більшості 7 входів є 111\???????<\000.
Грег Мартін

Нехай "bizarro" програми Stackylogic $ P $ є програмою $ BP $, сформованою шляхом перетворення порядку рядків оригінальних програм та зміни всіх 1s на 0s та віртуального вірша (але залишаючи? S та <alone). Здається, що вихід $ BP $ на входи $ b_1, b_2, \ крапки $ - НЕ виведення $ P $ на входи $! B_1,! B_2, \ dots $. Зауважте, що дані реалізації AND та OR пов'язані між собою, як NAND і NOR, так і дві версії XOR / XNOR. Деякі програми - це їх власне дивне (BUFFER, NOT, MEDIAN (v1)).
Грег Мартін

1
@GregMartin Yep. Я вважаю, що технічний термін - це подвійність .
Захоплення Кальвіна

Відповіді:


15

Сітківка , 79 78 73 68 66 65 63 62 55 44 байт

Кількість байтів передбачає кодування ISO 8859-1.

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

Введення здійснюється через STDIN і, як очікується, буде введенням користувача, відокремленим двома каналами ліній від вихідного коду.

Спробуйте в Інтернеті! (Перші два рядки включають тестовий набір, де кожен рядок є окремим тестовим випадком, а /не передачі рядків.)

Я не зовсім впевнений, що тут сталося. Це здається справді незграбним рішенням, і це насправді не проблема, з якою була створена Ретіна, але вона все одно перемагає всі поточні відповіді чомусь.

Пояснення

Остаточна версія цього насправді виявилася досить простою.

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

Перший етап - це просто цикл (завдяки +опції), який робить власне тлумачення мови. Етап - це єдина заміна регексу, але насправді це три різні заміни, видавлені на одну стадію, використовуючи той факт, що захоплення груп з невикористаних гілок просто вважається порожнім під час заміни.

  1. Обробка ?:

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

    Це просто приймає перший символ вводу, потім відповідає довільним символам, поки він не знайде ?<, і ставить цей перший символ перед <(видаленням ?).

  2. Обробка 0:

    (¶.*)0<
    <$1
    

    Це відповідає рядку, що передує a, 0<і ставить його після <, видаляючи 0. (Фактично, це просто видаляє 0та переміщує <одну лінію вгору.)

  3. Обробка 1:

    1<(¶.+)
    $1<
    

    Приблизно те саме, за винятком того, що ми переміщаємо <одну лінію вниз, видаляючи 1. Однією важливою деталлю, яку слід помітити, є використання +замість цього *, тобто нам потрібно, щоб наступний рядок не був порожнім.

Цікава частина - з'ясування того, чому це працює, і чому нам не потрібно відслідковувати останнє значення, яке ми вискакували, щоб визначити кінцевий вихід. Для цього нам потрібно розглянути, як може закінчитися вищевказана петля. Оскільки кожна можлива відповідність змінює рядок (оскільки принаймні один символ випадає з неї), нам потрібно лише розглянути випадки, коли збіг не вдається повністю.

Якщо персонаж перед ним <є ?єдиним способом, щоб матч провалився, це те, що ніде перед ним немає непередаваного символу, але це не може статися, тому що ми гарантуємо, що введення завжди достатньо.

Якщо символ в передній частині <IS 0, регулярний вираз буде завжди збігатися з , так як завжди є інша лінія над поточним (який може бути порожньою , що розділяє лінію введення з вихідного коду).

Якщо перед символом <стоїть символ 1, то регулярний вираз вийде з ладу, якщо ми знаходимося в останньому рядку (оскільки збіг не збігається), або якщо наступний рядок порожній (оскільки .+збіг не збігається). Зауважте, що обидва ці випадки відповідають припиненню програми після появи a 1.

Нарешті, є також можливість, якій <не передує жоден із ?01. Виявляється, ми можемо дійти до цієї ситуації лише за допомогою проскакувань a 0та переходу до порожнього рядка, так що <тепер передує передачі рядків.

Отже, коли програма припиняється на a 1, <все одно буде після цього 1. Але якщо програма припиняється на a 0, вона буде переведена на порожній рядок. Ми можемо легко перетворити цю інформацію в потрібний вихід простим етапом відповідності:

1<

Це просто рахує відповідність 1<у рядку. В силу сказаного вище міркування це буде , 1якщо програма завершується на 1, і 0якщо вона завершується на 0.


3
Ви, сер, чарівник.
GamrCorps

Такий Regex Mch Wow
Rohan Jhunjhunwala

12

Опукла , 102 95 байт

Ну а список мов на основі списку стеків, закодований мовою на основі стека, виявився досить важким. Позначте мої слова: я отримаю це до 100 байт або менше! EDIT: Успіх!

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

Спробуйте в Інтернеті!

Введення програми здійснюється через аргументи командного рядка. Введення 0s і 1s нормально (у TIO це означає, що у вікні "введення" розділено новий рядок).


Пояснення:

Весь код можна розділити на три частини:

N/S\+

Цей біт просто приймає програму введення та перетворює її в масив рядків, а також додає " "рядки до початку масиву. Оскільки масиви Convex обгортають, матимуть лише порожній стек на початку.

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

Ця частина визначає, з якого рядка (або стека) почати виконання. Він здійснює пошук у кожному рядку і вводить правильний номер стека в Mзмінну.

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

Це веселощі! Він постійно зациклюється, поки не досягне лінії, на якій є лише пробіл ( " ") (що символізує порожній стек). Якщо рядок не порожній, він робить наступне:

  1. Виставте останнього персонажа зі стека.
  2. Оператор переключення:
    1. Якщо символ є a ?, зробіть введення та додайте його до рядка.
    2. Якщо символом є a 0, перемістіть вказівник рядка вгору.
    3. Якщо символом є a 1, перемістіть вказівник рядка вниз.
    4. Якщо символом є (пробіл), надрукуйте найнедавніший елемент та кінцеву програму.

6

32-розрядний машинний код x86, 70 байт

У шістнадцятковій формі:

FC89E1565F31D28A07A8DF740B3C3C7511428D5C24FCEB0A5729142484C07405B20147EBE2578B3B8A17F6C2DF7414FF0B923C3F7501AC3C30750383C30883EB04EBE389CCC3

Вхід - це багаторядковий рядок, що закінчується NULL (відокремлений рядковим каналом), переданий через ESI. Вважається, що користувачем є перший рядок. Повертає "0" / "1" в AL.

Розбирання:

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

Припускаючи закінчення нового рядка в програмі

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

Менше гольфу

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

Тест

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 байт

1 байт завдяки @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

Пітон 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 як програма, z - введення. Так, мої імена змінних божевільні.


1

ES6, 190 байт

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
}

Використовуйте як f(program, input)


2
Кілька загальних порад щодо гольфу (десь є їх список): використовуйте [...o]замість цього o.split``, а використовуйте forзамість цього while, оскільки це дозволяє переміщувати два вирази в forдва байти. Кілька конкретних порад: Я вважаю, що ваш Numberакторський склад зайвий, оскільки це *2буде для вас, і я б просто прочитав, iвикористовуючи j=0і, на i[j++]мою думку, економить 11 байт.
Ніл

1
Вам не потрібні f=, анонімні функції дозволені.
gcampbell

0

Java, 256 255 231 219 215 213 байт

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

Демонстрація на Ideone.

Приймає програму та введення як аргументи та повертає результат у вигляді цілого числа.


@LeakyNun Змінено на forцикл, але що означає ваш перший коментар?
PurkkaKoodari

@ Pietu1998 LeakyNun означає, що це може бути, int f(String[]I)...і можна уникнутиString[]p=I.split("\n");
кішка

Це означає, що ви можете оголосити функцію якint f(String[]P)
Leaky Nun

1
@cat ninja'd на 7 секунд: /
Leaky Nun

Крім того, якщо ви влаштуєтесь на Java 8, у вас може бути лямбда на зразок (я думаю)->(String[]I){...
кіт

0

PHP (<7,0), 195 192 байт

Приймає програму як перший аргумент, а кожне значення - як додатковий аргумент.
Зауважте, що я тестував це з розділеними ("", ..) пробілами asn, а не новими рядками, але це все одно має працювати.
Повідомляє про застаріле повідомлення, якщо працювати в php> 5.3.
Також надає попередження, якщо ви виходите з верхньої частини програми. Однак він все ще працює і видає правильно, так що це добре.

<?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 не дуже добре справляється з обробкою струн, але це досить коротко.

Він працює, скануючи рядок курсору ( <), переміщаючи назад 1 місце, читаючи команду, замінюючи її tabсимволом та переміщуючи вперед або назад один рядок. Введення у вигляді масиву символів C, як char array[]="1\n?<\n11\n?\n0";result = f(array);, хоча, також, дозволено повернення каретки.

Хоча рядок введення модифікований, довжина не змінюється.

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

Тестова програма

Запустіть цю програму з кожним тестовим випадком як окремий параметр, використовуючи один зворотний проріз замість нових рядків. Тестові приклади будуть розділені порожнім рядком.

#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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.