Chạy Stackylogic


45

Stackylogic là một ngôn ngữ lập trình logic dựa trên tôi đã quyết rằng mất trong 0's và 1' s cho đầu vào và đầu ra một single 0hoặc 1hoàn thành thuận.

Một chương trình Stackylogic bao gồm các dòng chỉ có thể chứa ba ký tự 01?cũng như chính xác một dòng <ở cuối một trong các dòng. Các đường kẻ không được bỏ trống và phù hợp với quy <phải có ít nhất một 0, 1hoặc ?trước khi nó.

Đây là một chương trình mẫu mà (như tôi sẽ giải thích) tính toán NAND của hai bit:

1
?<
11
?
0

Mỗi dòng trong chương trình Stackylogic được coi là một ngăn xếp , với đáy ở bên trái và trên cùng bên phải. Ngẫu nhiên, có một ngăn xếp trống (dòng trống) trước dòng đầu tiên trong một chương trình và sau dòng cuối cùng.

Cái <mà chúng ta sẽ gọi con trỏ , đánh dấu ngăn xếp để bắt đầu khi chương trình Stackylogic được chạy. Việc thực hiện chương trình Stackylogic được tiến hành như sau:

  1. Bật ký tự trên cùng khỏi ngăn xếp mà con trỏ hiện đang trỏ tới.

    • Nếu ký tự là ?, nhắc người dùng cho a 0hoặc a 1và hành động như thể đó là ký tự.
    • Nếu ký tự là 0, di chuyển con trỏ một chồng lên (đến dòng phía trên dòng hiện tại).
    • Nếu ký tự là 1, di chuyển con trỏ xuống một chồng (đến dòng bên dưới dòng hiện tại).
  2. Nếu ngăn xếp con trỏ di chuyển đến trống, hãy xuất giá trị cuối cùng được bật ra khỏi ngăn xếp (luôn luôn là một 0hoặc 1) và kết thúc chương trình.

  3. Khác, nếu ngăn xếp con trỏ di chuyển đến không trống, quay lại bước 1 và lặp lại quy trình.

Lưu ý rằng các chương trình Stackylogic luôn kết thúc vì cuối cùng chúng phải làm cạn kiệt các ngăn xếp của chúng.

Ví dụ NAND

Trong chương trình NAND, con trỏ bắt đầu trên ?:

1
?<
11
?
0

Chúng tôi sẽ giả sử người dùng nhập một 1lần khi ?bật, điều đó có nghĩa là con trỏ sẽ di chuyển xuống, làm cho chương trình trông như thế này:

1

11<
?
0

Bây giờ một đồng bằng 1nằm ở đầu ngăn xếp con trỏ. Nó được bật hợp lệ và con trỏ di chuyển lại:

1

1
?<
0

Bây giờ giả sử đầu vào của người dùng 0cho ?, có nghĩa là con trỏ sẽ di chuyển lên:

1

1<

0

Một lần nữa, a 1nằm trên ngăn xếp con trỏ để con trỏ bật lên và di chuyển xuống:

1


<
0

Cuối cùng, ngăn xếp con trỏ trống, vì vậy giá trị cuối cùng xuất hiện, là 1, là đầu ra và chương trình kết thúc.

Đây là chính xác cho một cổng NAND vì 1 NAND 01. Điều này tất nhiên hoạt động cho ba đầu vào hai bit khác nếu bạn quan tâm để kiểm tra.

HOẶC Ví dụ

Chương trình Stackylogic này mô phỏng một cổng OR :

?
?<

Thật dễ dàng để thấy rằng một đầu vào ban đầu 1sẽ đẩy con trỏ đến ngăn trống ẩn bên dưới dòng cuối cùng, kết thúc chương trình và xuất ra cái 1chỉ là đầu vào.

00Mặt khác, đối với đầu vào , con trỏ sẽ di chuyển đến ngăn xếp trống ẩn ở trên cùng, kết thúc chương trình và xuất kết quả cuối cùng 0thành đầu vào.

Thử thách

Viết chương trình hoặc hàm nhận trong chương trình Stackylogic dưới dạng chuỗi và chạy nó, in hoặc trả về kết quả 0hoặc 1.

Theo ?, bạn có thể nhắc người dùng nhập 0hoặc 1nhập hoặc đọc giá trị từ một chuỗi đặt trước của 01các mà bạn cũng lấy làm đầu vào. (Đây có thể là một chuỗi đầu vào khác cho chương trình / chức năng của bạn hoặc bạn chỉ có thể giả sử dòng đầu tiên hoặc cuối cùng của chuỗi chương trình sẽ là luồng đầu vào).

Bạn có thể giả định chương trình và đầu vào luôn được hình thành tốt. Bạn có thể tùy ý giả sử các chương trình đầu vào đi kèm với một dòng mới duy nhất (mặc dù hãy nhớ luôn có một ngăn xếp ngầm ẩn ở cuối).

Mã ngắn nhất tính bằng byte thắng.

Chương trình mẫu khác

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

Cảm ơn Martin cho các chương trình trung bình .


Nếu bạn muốn thêm chức năng 3 đầu vào, đây là một cách để thực hiện trung vị : ?\1?<\??. Ngoài ra, đây là một triển khai 5 dòng đối xứng:?\?0\?<\?1\?
Martin Ender

Ồ, tôi tìm thấy một triển khai thậm chí gọn gàng hơn : 1\???<\0.
Martin Ender

2
@ MartinEnder thực hiện gọn gàng hơn chức năng trung bình 3 đầu vào (tương đương, chức năng đa quy tắc) khái quát độc đáo. Ví dụ, hàm đa quy tắc 7 đầu vào là 111\???????<\000.
Greg Martin

Đặt "bizarro" của chương trình Stackylogic $ P $ là chương trình $ BP $ được hình thành bằng cách đảo ngược thứ tự các dòng của chương trình gốc, và thay đổi tất cả 1 giây thành 0 và ngược lại (nhưng để lại một mình và <một mình). Có vẻ như đầu ra của $ BP $ trên các đầu vào $ b_1, b_2, \ dot $ không phải là đầu ra của $ P $ trên các đầu vào $! B_1 ,! B_2, \ dot $. Lưu ý rằng các triển khai AND và OR đã cho có liên quan đến bizarro theo cách này, cũng như NAND và NOR, và hai phiên bản XOR / XNOR. Một số chương trình là bizarro của riêng họ (BUFFER, KHÔNG, MEDIAN (v1)).
Greg Martin

1
@GregMartin Yep. Tôi tin rằng thuật ngữ kỹ thuật là nhị nguyên .
Sở thích của Calvin

Câu trả lời:


15

Võng mạc , 79 78 73 68 66 65 63 62 55 44 byte

Số lượng byte giả định mã hóa ISO 8859-1.

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

Đầu vào thông qua STDIN và dự kiến ​​là đầu vào của người dùng được phân tách bằng hai nguồn cấp dữ liệu từ mã nguồn.

Hãy thử trực tuyến! (Hai dòng đầu tiên cho phép một bộ thử nghiệm, trong đó mỗi dòng là một trường hợp thử nghiệm riêng /thay vì các dòng thử nghiệm .)

Tôi không hoàn toàn chắc chắn những gì đã xảy ra ở đây. Cảm giác này giống như một giải pháp thực sự khó hiểu và đây thực sự không phải là vấn đề mà Retina đã đưa ra nhưng nó vẫn đánh bại tất cả các câu trả lời hiện tại vì một số lý do.

Giải trình

Phiên bản cuối cùng này thực sự đã kết thúc khá đơn giản.

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

Giai đoạn đầu tiên chỉ đơn giản là một vòng lặp (do +tùy chọn) thực hiện việc diễn giải ngôn ngữ thực tế. Giai đoạn này là một sự thay thế regex duy nhất, nhưng trên thực tế, đó thực sự là ba sự thay thế khác nhau được nén thành một giai đoạn, bằng cách sử dụng thực tế là bắt các nhóm từ các nhánh không sử dụng được coi là trống rỗng trong quá trình thay thế.

  1. Chế biến ?:

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

    Điều này chỉ đơn giản là lấy ký tự đầu tiên của đầu vào, sau đó khớp các ký tự tùy ý cho đến khi tìm thấy ?<và đặt ký tự đầu tiên đó ở phía trước <(xóa ?).

  2. Chế biến 0:

    (¶.*)0<
    <$1
    

    Điều này khớp với dòng trước a 0<và đặt nó sau <, loại bỏ 0. (Hiệu quả, điều này chỉ xóa 0và di chuyển <một dòng lên trên.)

  3. Chế biến 1:

    1<(¶.+)
    $1<
    

    Khá nhiều điều tương tự, ngoại trừ việc chúng ta di chuyển <xuống một dòng trong khi xóa a 1. Một chi tiết quan trọng cần chú ý là việc sử dụng +thay vì *, đó là, chúng tôi yêu cầu dòng tiếp theo không được để trống.

Phần thú vị là tìm hiểu lý do tại sao điều này hoạt động và tại sao chúng ta không cần theo dõi giá trị cuối cùng mà chúng ta đã bật để xác định đầu ra cuối cùng. Để làm điều đó, chúng ta cần xem xét làm thế nào vòng lặp trên có thể chấm dứt. Vì mọi trận đấu có thể thay đổi chuỗi (vì ít nhất một ký tự được loại bỏ khỏi chuỗi đó), chúng tôi chỉ cần xem xét các trường hợp khớp hoàn toàn không thành công.

Nếu nhân vật trước mặt <?cách duy nhất để trận đấu thất bại là không có nhân vật không phải là nguồn cấp dữ liệu ở bất cứ đâu trước mặt nó, nhưng điều đó không thể xảy ra vì chúng tôi đảm bảo rằng luôn có đủ đầu vào.

Nếu ký tự ở phía trước <0, biểu thức chính quy sẽ luôn khớp, vì luôn có một dòng khác phía trên dòng hiện tại (có thể là dòng trống tách dòng đầu vào khỏi mã nguồn).

Nếu ký tự ở phía trước <1, biểu thức chính quy sẽ thất bại nếu chúng ta ở dòng cuối cùng (vì sẽ không khớp) hoặc nếu dòng tiếp theo trống (vì .+sẽ không khớp). Lưu ý rằng cả hai trường hợp này đều tương ứng với việc chấm dứt chương trình sau khi xuất hiện a 1.

Cuối cùng, cũng có khả năng <không có trước ?01. Hóa ra chúng ta chỉ có thể đạt được tình huống này bằng cách bật một 0và di chuyển đến một dòng trống, do đó <hiện tại được đi trước bởi một nguồn cấp dữ liệu.

Vì vậy, khi chương trình kết thúc vào a 1, <vẫn sẽ có sau đó 1. Nhưng nếu chương trình chấm dứt trên a 0, nó sẽ chuyển sang một dòng trống. Chúng ta có thể dễ dàng biến thông tin này thành đầu ra mong muốn với một giai đoạn khớp đơn giản:

1<

Điều này chỉ đơn giản là đếm các trận đấu 1<trong chuỗi. Theo lý do trên, điều này sẽ xảy ra 1nếu chương trình kết thúc vào a 10nếu nó kết thúc vào a 0.


3
Thưa ngài, là một phù thủy.
GamrCorps

Regex Mch Wow
Rohan Jhunjhunwala

12

Lồi , 102 95 byte

Chà, một ngôn ngữ dựa trên danh sách ngăn xếp được mã hóa bằng ngôn ngữ dựa trên ngăn xếp hóa ra khá khó khăn. Đánh dấu các từ của tôi: Tôi sẽ nhận được điều này đến 100 byte hoặc ít hơn! EDIT: Thành công!

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

Hãy thử trực tuyến!

Đầu vào chương trình là thông qua các đối số dòng lệnh. Đầu vào 0s và 1s bình thường (trên TIO, điều này có nghĩa là phân tách dòng mới trong hộp "đầu vào").


Giải trình:

Tất cả các mã có thể được chia thành ba phần:

N/S\+

Bit này chỉ đơn giản là lấy chương trình đầu vào và chuyển đổi nó thành một mảng các dòng và cũng thêm " "các dòng vào đầu mảng. Vì các mảng của Convex bao bọc, chỉ có một ngăn xếp trống tại cầu xin sẽ làm.

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

Phần này xác định dòng (hoặc ngăn xếp) nào để bắt đầu thực hiện. Nó tìm kiếm thông qua mỗi dòng và đặt số ngăn xếp chính xác vào Mbiến.

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

Đây là một chút thú vị! Nó liên tục lặp cho đến khi nó đạt đến một dòng chỉ có một khoảng " "trắng ( ) trên đó (tượng trưng cho một ngăn xếp trống). Nếu dòng không trống, nó thực hiện như sau:

  1. Pop nhân vật cuối cùng ra khỏi ngăn xếp.
  2. Tuyên bố chuyển đổi:
    1. Nếu ký tự là một ?, lấy đầu vào và nối ký tự đó vào dòng.
    2. Nếu ký tự là a 0, di chuyển con trỏ dòng lên một.
    3. Nếu ký tự là a 1, di chuyển con trỏ xuống một.
    4. Nếu ký tự là một (khoảng trắng), hãy in chương trình kết thúc và kết thúc gần đây nhất.

6

Mã máy 32 bit x86, 70 byte

Trong hex:

FC89E1565F31D28A07A8DF740B3C3C7511428D5C24FCEB0A5729142484C07405B20147EBE2578B3B8A17F6C2DF7414FF0B923C3F7501AC3C30750383C30883EB04EBE389CCC3

Đầu vào là một chuỗi nhiều dòng kết thúc NULL (được phân tách theo dòng) được truyền qua ESI. Đầu vào của người dùng được coi là dòng đầu tiên. Trả về '0' / '1' trong AL.

Tháo gỡ:

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

Giả sử một dòng mới chấm dứt trong chương trình

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

Ít chơi gôn

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

Kiểm tra

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 byte

1 byte nhờ @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

Con tră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 là chương trình, z là đầu vào. Vâng, tên biến của tôi là điên rồ.


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
}

Sử dụng như f(program, input)


2
Một vài mẹo chơi gôn chung (có một danh sách những điều này ở đâu đó): sử dụng [...o]thay vì o.split``và sử dụng forthay vì while, vì điều đó cho phép bạn di chuyển hai biểu thức vào việc forlưu hai byte. Một vài lời khuyên cụ thể: Tôi nghĩ rằng Numberdàn diễn viên của bạn là không cần thiết, vì ý *2chí sẽ dành cho bạn, và tôi sẽ chỉ đọc ibằng cách sử dụng j=0i[j++]tôi nghĩ tiết kiệm được 11 byte.
Neil

1
Bạn không cần f=, các chức năng ẩn danh được cho phép.
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;}

Bản demo trên Ideone.

Lấy chương trình và đầu vào làm đối số và trả về kết quả dưới dạng số nguyên.


@LeakyNun Thay đổi thành một forvòng lặp, nhưng nhận xét đầu tiên của bạn có ý nghĩa gì?
PurkkaKoodari

@ Pietu1998 LeakyNun có nghĩa là nó có thể int f(String[]I)...và bạn có thể tránhString[]p=I.split("\n");
con mèo

Nó có nghĩa là bạn có thể khai báo hàm làint f(String[]P)
Leaky Nun

1
@cat ninja'd 7 giây: /
Leaky Nun

Ngoài ra, nếu bạn giải quyết Java 8, bạn có thể có một lambda như (tôi nghĩ)->(String[]I){...
con mèo

0

PHP (<7.0), 195 192 byte

Lấy chương trình làm đối số đầu tiên và mỗi giá trị làm đối số bổ sung.
Lưu ý rằng tôi đã thử nghiệm điều này với các khoảng trắng phân tách ("", ..) thay vì các dòng mới nhưng dù sao nó cũng sẽ hoạt động.
Đưa ra một thông báo không dùng nữa nếu chạy trong php> 5.3.
Cũng đưa ra một cảnh báo nếu bạn đi ra khỏi đầu chương trình. Tuy nhiên nó vẫn hoạt động và đầu ra chính xác vì vậy nó ổn.

<?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 không làm tốt lắm với thao tác chuỗi, nhưng điều này khá ngắn.

Nó hoạt động bằng cách quét chuỗi cho con trỏ ( <), di chuyển trở lại 1 vị trí, đọc lệnh, thay thế nó bằng một tabký tự và di chuyển tiến hoặc lùi một dòng. Đầu vào ở dạng mảng C char, giống như char array[]="1\n?<\n11\n?\n0";result = f(array);, mặc dù trả về vận chuyển cũng được cho phép.

Mặc dù chuỗi đầu vào được sửa đổi, độ dài không thay đổi.

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

Chương trình kiểm tra

Chạy chương trình này với mỗi trường hợp thử nghiệm dưới dạng một tham số riêng biệt, sử dụng dấu gạch chéo ngược duy nhất thay cho dòng mới. Các trường hợp thử nghiệm sẽ được phân tách bằng một dòng trống.

#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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.