Đánh giá một biểu thức của các nhà khai thác ternary


29

Hãy xem xét một ngữ pháp qua bảng chữ cái { 0, 1, ?, :} xác định bởi các quy tắc sản xuất

s → 010 ?s :s ┃ 1 ?s :s

Đưa ra một chuỗi được tạo từ s , phân tích nó như là một biểu thức trong đó ?:là liên kết đúng (ví dụ: a?B?X:Y:c?d:e?f:gphương tiện a?(B?X:Y):(c?d:(e?f:g))) và đánh giá nó với các ngữ nghĩa sau:

eval(0) = 0
eval(1) = 1
eval(0?a:b) = eval(b)
eval(1?a:b) = eval(a)

Nếu kết quả là 0 , xuất một số giá trị cố định; nếu đầu ra là 1 , xuất ra một giá trị cố định khác nhau. Chỉ định các giá trị đầu ra bạn đã chọn (ví dụ 0/ 1, hoặc False/ True) trong câu trả lời của bạn.

Các trường hợp thử nghiệm

0 -> 0
1 -> 1
0?0:1 -> 1
0?1:0 -> 0
1?0:1 -> 0
1?1:0 -> 1
0?1?0:1:1 -> 1
1?0?1:1:1 -> 1
1?0:1?0:1?1:1 -> 0
1?1?1:0?1?0:0:0:0 -> 1
1?0:1?0?1:1?1:0:1?1?1:1:1?0:1 -> 0
1?1?1:0?0?1:1:0?1:0:1?1?0?0:0:1?1:0:0?1?0:1:1?0:1 -> 1
0?0?1?0?0:1:0?0:0:0?0?1:1:1?0:1:0?0?0?1:0:0?1:1:1?1?0:1:1 -> 0

Quy tắc

  • Bạn không được sử dụng các ngôn ngữ dựng sẵn diễn giải các chuỗi dưới dạng mã trong một số ngôn ngữ lập trình và chạy nó (chẳng hạn như JavaScript / Perl / Ruby / Python eval).
  • Điều đó nói rằng, mã của bạn không thực sự phải phân tích cú pháp và sau đó đánh giá chuỗi đầu vào. Bạn có thể thực hiện bất kỳ phương pháp nào để đạt được kết quả tương đương và không vi phạm quy tắc trước đó.
  • Chương trình của bạn sẽ được kiểm tra chống lại perl -le 'print eval<>'.
  • Mã ngắn nhất (tính bằng byte) sẽ thắng.

Làm thế nào về việc sử dụng ngôn ngữ tích hợp như eval diễn giải các chuỗi dưới dạng mã $ my_lingu sau khi thay đổi hoàn toàn chuỗi?
Adám

Điều gì về các nội dung diễn giải các chuỗi dưới dạng mã $ some_other_lingu ?
Mego

@ Adám Điều đó sẽ không được phép, xin lỗi.
Lynn

@Mego Hmm, có một cơ hội gian lận tầm thường ở đó, vì vậy tôi đã mở rộng quy tắc để bao gồm tất cả các nội dung được xây dựng như vậy.
Lynn

1
Trong ánh sáng của trường hợp thử nghiệm của Martin, có lẽ nó sẽ đơn giản hơn để xác định ngữ pháp như S → T | T ? S : S, T → 0 | 1, loại bỏ sự cần thiết phải nói về associativity?
Peter Taylor

Câu trả lời:



17

Võng mạc , 23 byte

r-1=+`0\?.:|1\?(.):.
$1

Hãy thử trực tuyến! (Dòng đầu tiên cho phép bộ kiểm tra được phân tách bằng nguồn cấp.)

Giải trình

Nó thực sự khá đơn giản. Đầu vào được giảm xuống kết quả bằng cách lặp đi lặp lại ( +) đánh giá các ternary chỉ chứa chữ. Để đảm bảo việc này được thực hiện theo cách kết hợp phải, chúng tôi tìm kiếm các kết quả khớp từ phải sang trái ( r) và chỉ thay thế trận đấu cuối cùng mà chúng tôi tìm thấy ( -1=).

Bản thân regex khớp hoặc 0\?.:xóa nó (chỉ để lại các thứ sau :) hoặc 1\?.:.thay thế nó bằng giá trị sau ?.


Nếu regex bắt đầu từ bên phải thì bạn không nên xử lý 1trận đấu st thay vì -1st?
Nữ tu bị rò rỉ

@LeakyNun Thật không may, tôi nghĩ rằng tôi đảo ngược các trận đấu trước khi áp dụng giới hạn.
Martin Ender

10

Haskell, 106 101 100 90 83 byte

Điều này phụ thuộc rất nhiều vào khả năng khớp mẫu của mẫu Haskell. Trước hết, chúng tôi đảo ngược chuỗi sao cho chúng tôi chỉ có thể tìm kiếm cho lần xuất hiện đầu tiên của b:a?x(thường sẽ đọc là x?a:b) và thay thế nó bằng giá trị của nó. Điều này tự động cung cấp sự kết hợp đúng . Ở đây chúng tôi sử dụng x:xsmô hình. Đây là những gì chức năng fđang làm. Sau đó, về cơ bản, chúng tôi áp dụng fcho đầu ra của nó nhiều lần, cho đến khi chúng tôi chỉ còn một số (0 hoặc 1).

Cảm ơn @Lynn vì 12 byte!

f(b:':':a:'?':x:l)|x>'0'=a:l|1>0=b:l
f(x:l)=x:f l
f x=x
until((<2).length)f.reverse

8

Brainfuck, 82 64 63 byte

+
[
  ,>>,>
  +++++++[<---<<-[------>>]<-]
  <<
  [
    ->[<++>[+]]
  ]
  +>[>>]
  <<<-
]
<.

Đầu ra là \xffcho 0\x00cho 1. Việc thực hiện brainfuck phải cho phép đi về bên trái của ô bắt đầu.

Điều này về cơ bản sử dụng cách tiếp cận tương tự như câu trả lời Python của xsot , nhưng việc phân nhánh có lẽ khó theo dõi hơn so với lần gửi 82 byte ban đầu của tôi:

-
[
  +
  [
    ->,,>+++++++[<--------->-]
    <[<++>[+]]
    <
  ]
  ,->,>+++++++[<[-------<]>>-->-]
  <[<]
  <
]
>>.

(Đối với giải pháp này, đầu ra là \xfecho 0\xffcho 1, và khả năng tương thích rộng hơn đạt được khi đầu vào kết thúc bằng một dòng mới.)

Nếu bạn không thể bận tâm phân tích giải pháp của xsot, ý tưởng là thế này: Tiếp tục từ trái sang phải. Nếu bạn thấy 1?sau đó tham lam loại bỏ nó. Nếu bạn thấy 0?sau đó loại bỏ tất cả mọi thứ giữa đó và tương ứng :. Khi ?không xuất hiện dưới dạng ký tự thứ hai, dừng lặp và in ký tự đầu tiên của chuỗi còn lại.

Vì vậy, giải pháp 82 byte thực sự phản ánh sơ đồ đó khá chặt chẽ. Vòng lặp bên trong xử lý 0?, giống như vòng lặp bên trong của xsot. Một số cẩn thận được thực hiện để vào vòng lặp chính mà không kiểm tra bất kỳ ký tự đầu vào nào; tức là, chúng tôi muốn kiểm tra xem ký tự thứ hai ?chỉ là một lần ở cuối vòng lặp chính hay không, cũng không phải ở đầu trước khi vào vòng lặp chính.

Giải pháp 63 byte về cơ bản kết hợp các vòng lặp bên trong và bên ngoài thành một, điều mà tôi nghi ngờ là có thể có sự tương đồng giữa các vòng lặp đó. Bố cục bộ nhớ trong vòng lặp chính có thể được mô tả là:

[s] d c

trong đó [x]có nghĩa là ô hiện tại - sbắt đầu như một giá trị khác không giả, chỉ ra rằng chúng ta vẫn đang lặp và ngay lập tức được ghi đè bằng một ký tự đầu vào (hoặc 0hoặc 1). Các dtế bào giữ (tiêu cực) sâu trong trường hợp chúng ta đang ở giữa một 0?, nếu không 0. Điều cnày sẽ là ?hoặc :hoặc dòng mới hoặc EOF.

Sau khi cập nhật sc, chúng tôi xử lý 0?trường hợp bằng cách cập nhật dtương ứng và điều chỉnh con trỏ, nếu không, chúng tôi sử dụng giá trị hiện tại clà giá trị của dlần lặp tiếp theo hoặc dừng nếu chúng tôi hoàn thành.


7

Python 2, 76 74 73 72 byte

a=`id`
for c in input()[::-1]:a=(c+a,a[ord(c)*7%9]+a[4:])[a>'?']
print a

Với đầu vào là chuỗi ký tự để tránh raw_.

Đầu ra là 0hoặc 1theo sau <built-in function id>.


1
Haha, tôi vừa đọc câu trả lời của bạn cho b lang và chuẩn bị đăng một câu trả lời gần như giống hệt nhau! Đây là một tối ưu hóa bổ sung:3>>int(c)
xsot

Quan tâm để giải thích làm thế nào điều này hoạt động? Trông rất gọn gàng
WorldSEnder

@WorldSEnder Tôi nghĩ rằng đó là loại giải pháp có thể khó đưa ra, nhưng dễ hiểu khi bạn nhìn thấy nó. Nó chạy qua chuỗi ngược và liên tục xử lý điều kiện ngoài cùng bên phải, như các bộ giải khác cũng đã làm như vậy.
Mitch Schwartz

Đó là `id`trò lừa! Làm tốt lắm :)
Lynn

5

Python 2, 89 byte

s=input()
while'?'<=s[1:]:
 n=s<'1'
 while n:s=s[2:];n+=-(s[1]<'?')|1
 s=s[2:]
print s[0]

Đầu vào được thực hiện dưới dạng một chuỗi ký tự.


5

Grime , 34 31 byte

E=d|d\?E.E
e`\1|\1\?_.E|\0\?E._

In 1cho đầu vào trung thực và 0cho những người giả mạo . Hãy thử trực tuyến! Trường hợp thử nghiệm cuối cùng không may hết bộ nhớ trên TIO.

Giải trình

Tính kết hợp đúng về cơ bản có nghĩa là trong a?b:c, aluôn luôn 0hoặc 1, không bao giờ là một biểu thức dài hơn. Tôi sẽ chỉ định nghĩa đệ quy một mẫu phù hợp với biểu thức trung thực như thế và kiểm tra đầu vào so với mẫu đó. Cũng không cần thiết phải kiểm tra xem mọi thứ :có thực sự là a không :, nếu ?tất cả các s đều được kiểm tra: có một số ?s và :s bằng nhau trong đầu vào, và nếu một số ?được phân loại không chính xác là a :, thì tương ứng :sẽ không khớp và khớp với Grime động cơ sẽ quay lại.

E=d|d\?E.E
E=                      Define nonterminal E (for "expression") as
  d|                     a digit, OR
    d                    a digit,
     \?                  a literal ?,
       E                 a match of E,
        .                any character (will match a :), and
         E               another match of E.
e`\1|\1\?_.E|\0\?E._
e`                      Match entire input against this pattern (truthy expression):
  \1|                    a literal 1, OR
     \1\?                a literal 1?,
         _               a recursive match of truthy expression,
          .              any character (will match a :), and
           E|            any expression, OR
             \0\?E._     the same, but with 0 in front, and _ and E swapped.

5

Haskell, 79 71 70 62 60 56 byte

Chỉnh sửa: Cảm ơn @Zgarb cho 3 byte và @nimi cho 4 byte!

e(x:'?':r)|a:_:s<-e r=last$e s:[a:tail(e s)|x>'0']
e x=x

Đây là một cách tiếp cận đệ quy mà phần nào lạm dụng quy tắc đầu ra "một số giá trị cố định" . Chỉnh sửa: Loại bỏ các bộ dữ liệu không chỉ tiết kiệm 8 byte, nó cũng mang lại một đầu ra đẹp hơn: "0"hoặc "1".

Phiên bản bị đánh cắp:

eval (x:'?':r1) = if x=='1' then (a, r3) else (b, r3)
    where (a,':':r2) = eval r1
          (b, r3)    = eval r2
eval (x:r) = (x,r)

Làm thế nào nó hoạt động?
Các evalchức năng đi qua cây tiềm ẩn của các biểu thức

eval 1?0?0:1:0?1:0 -> eval 1?          :
                             eval 0?0:1 eval 0?1:0

và trả về một tuple của mẫu (result, rest).
Mẫu đầu tiên (x:'?':r1)phù hợp xvới '1'r1đến "0?0:1:0?1:0". Áp dụng đệ quy evalđể r1đánh giá biểu thức con 0?0:1và trả về (0,":0?1:0"). Phù hợp với điều này cho (a,':':r2)năng suất mẫu a=0r2=0?1:0. Công thức phụ này cũng được đánh giá đệ quy sao cho b='0'r3="". Kiểm tra nếu x'1'hoặc '0'và trả lại (a, r3)hoặc (b, r3).


1
Cách tiếp cận tốt đẹp! Sẽ x>'0'làm việc thay x=='1'thế?
Zgarb

Cảm ơn, tôi đã không nghĩ về điều đó trong khi xử lý ký tự.
Laikoni

1
Tôi nghĩ bạn cũng có thể thay thế ':'bằng _.
Zgarb

Có, sau đó mã thậm chí hoạt động cho các dấu phân cách tùy ý thay vì chỉ :. Cảm ơn một lần nữa!
Laikoni

1
Tốt đẹp! Bạn có thể thay thế if .. then .. elsebằng last$e s:[a:tail(e s)|x>'0'].
nimi

3

JavaScript (ES6), 53 byte

f=s=>s[1]?f(s.replace(/0\?.:|1\?(.):.(?!\?)/,"$1")):s

Trả về 0hoặc 1cho đầu vào hợp lệ; treo cho đầu vào không hợp lệ. Giải thích: vì việc đảo ngược một chuỗi là khó xử trong JavaScript, lần thử 71 byte đầu tiên của tôi đã hoạt động bằng cách sử dụng giao diện phủ định cho một chuỗi khác ?, điều này sẽ làm xáo trộn tính kết hợp:

f=s=>s[1]?f(s.replace(/(.)\?(.):(.)(?!\?)/,(_,a,b,c)=>+a?b:c)):s

Vì điều này hơi lâu, tôi tự hỏi liệu tôi có thể cải thiện vấn đề hay không bằng cách kết hợp việc đưa ra quyết định vào regrec. Khi nó bật ra, nó không phải là một thành công ngay lập tức, vì nó cũng mất 71 byte:

f=s=>s[1]?f(s.replace(/0\?.:(.)(?!\?)|1\?(.):.(?!\?)/,"$1$2")):s

Sau đó, nó xảy ra với tôi rằng 0?0:0?1:luôn luôn là không có, không quan tâm đến sự kết hợp. Điều này giúp tôi tiết kiệm gần 25%.


Khối mã của bạn ở đầu bị thiếu f=. Tôi chưa kiểm tra xem số byte của bạn có tính đến nó hay không.
Patrick Roberts

@PatrickRoberts Tôi mãi mãi làm điều đó, bởi vì tôi sao chép từ nhật ký, điều này chỉ hiển thị kết quả của bài tập (tất nhiên là đủ cho các chức năng không hồi quy).
Neil

@ Nếu bạn có thể sao chép từ đầu vào nhật ký thay vì đầu ra
ASCII chỉ có

@MarsUltor Đầu vào nhật ký bao gồm lời nhắc, sau đó tôi phải nhớ để loại trừ. Đây là một bước bổ sung khó xử cho các hàm không đệ quy, đó là lý do tại sao tôi sao chép từ đầu ra theo mặc định.
Neil

3

Perl, 32 + 1 ( -pcờ) = 33 byte

Tín dụng đầy đủ cho @Mitch Swartch , vì giải pháp của anh ta ngắn hơn 14 byte so với của tôi!
Cũng xin cảm ơn @Neil , người đã đề xuất giải pháp dài hơn 1 byte so với Mitch.

s/.*\K(0\?..|1\?(.)..)/\2/&&redo

Cần -pcờ, cũng như -M5.010hoặc -Eđể chạy. Ví dụ :

perl -pE 's/.*\K(0\?..|1\?(.)..)/\2/&&redo' <<< "0
0?0:1
0?1?0:1:1
1?0:1?0?1:1?1:0:1?1?1:1:1?0:1
0?0?1?0?0:1:0?0:0:0?0?1:1:1?0:1:0?0?0?1:0:0?1:1:1?1?0:1:1"

Giải thích : Về cơ bản, nó làm giảm các khối a?b:c(bắt đầu từ cuối để chắc chắn không có phần ?sau) thành bhoặc ctùy thuộc vào tính trung thực của a, lặp đi lặp lại cho đến khi chuỗi chỉ chứa 1hoặc 0.


Không -tính vào điểm số của bạn? Hrm. Thú vị ... Câu trả lời tốt!
MayorMonty

@MayorMonty Đối với 1 lớp lót, bạn có thể gọi nó trên dòng lệnh bằng cách perl -e '<code>'thêm pchi phí chỉ 1 byte perl -pe '<code>'.
Neil

@Neil Ahh, điều đó có ý nghĩa
MayorMonty

Trên thực tế, bạn không phải đảo ngược chuỗi, bạn chỉ có thể nhìn tiêu cực cho a ?, tôi đã có thể cắt giảm xuống còn 34 byte theo cách này.
Neil

Đây là 32 + 1:s/.*\K(1\?(.)..|0\?..)/\2/&&redo
Mitch Schwartz

2

Con trăn 3, 93 69 byte

def f(s):z=s.pop;r=z(0);return s and':'<z(0)and(f(s),f(s))[r<'1']or r

Đầu vào là chuỗi dưới dạng danh sách các ký tự, đầu ra là "0"hoặc"1"

>>>f(list("0?0:1"))
<<<"1"

Phiên bản bị đánh cắp:

def parse(s):
    predicate = s.pop(0)
    if s and s.pop(0) == '?':
        left, right = parse(s), parse(s)
        if predicate == '0':
            return right
        return left
    return predicate

Một lần thử khác, nhưng với nhiều byte hơn đáng kể:

i=input()[::-1]
a=[i[0]]
for o,z in zip(i[1::2],i[2::2]):a+=[z]if o<'?' else[[a.pop(),a.pop()][z>'0']]
print(a[0])

Câu trả lời của bạn có thể là một chức năng - bạn có thể xóa dòng thứ hai.
Lynn

Điều này rõ ràng chưa được kiểm tra, vì nó không vượt qua các trường hợp thử nghiệm và phiên bản không được cung cấp cho lỗi thời gian chạy. Ý tưởng cơ bản của bạn là tốt mặc dù. Với một số điều chỉnh, tôi nhận được 68 trong Python 2 và 69 trong Python 3.
Mitch Schwartz

1
Vâng, tôi nghĩ sẽ có ý nghĩa hơn khi tôi đưa ra câu trả lời cho bạn hơn là chỉnh sửa nó thành của riêng tôi (vì tôi đã không nghĩ về cách tiếp cận này trước khi tôi thấy câu trả lời của bạn) hoặc ngồi đợi trong khi câu trả lời của bạn có lỗi. Đây là 68 tôi đã đề cập def f(s):x=s.pop(0);return[]<s<s.pop(0)>'>'and(f(s),f(s))[x<'1']or xvà đối với Python 3 với khoảng cách chỉnh sửa nhỏ cho bạn, có def f(s):z=s.pop;r=z(0);return s and':'<z(0)and(f(s),f(s))[r<'1']or r.
Mitch Schwartz

Cảm ơn @MitchSchwartz, khá nhiều phân tích từ bên phải, thay vì bên trái, gotcha
WorldSEnder

1
Mặt khác, trái thay vì phải, ~~~
WorldSEnder

1

SED, 75 74 68 (40 + 1 cho -r) 41

:
s,(.*)1\?(.):.,\1\2,
s,(.*)0\?.:,\1,
t

Bạn có thể cắt giảm điều này bằng cách sử dụng thủ thuật của @ MitchSchwartz trong bình luận của anh ấy , mặc dù bạn có thể phải sử dụng (.*)và thêm một thuật ngữ thay thế bổ sung.
Neil

@Neil, bạn có thể đúng, nhưng tôi không thể tìm ra cách làm cho nó hoạt động.
Riley

Tôi đã viết nó trong trò chuyện, vì định dạng trong một nhận xét có thể gây nhầm lẫn: chat.stackexchange.com/transcript/message/31709640#31709640
Mitch Schwartz

@MitchSchwartz Heh, nhãn trống hoạt động? Nhưng tôi nghĩ bạn có nghĩa là \3thay vì \2. Ngoài ra, bạn có thể tham gia các dòng với ;để có được :;s/(.*)(1\?(.):.|0\?.:)/\1\3/;t.
Neil

@Neil Tôi đã có \3như vậy có nghĩa là tôi đã vô tình sao chép một phiên bản trước đó. Tôi biết về dấu chấm phẩy. Nhưng yuck, tại sao bạn muốn sử dụng dấu chấm phẩy.
Mitch Schwartz

0

Tiện ích Bash + GNU, 42

rev|sed -r ':
s/(.):.\?0|.:(.)\?1/\1\2/
t'

Ý tưởng tương tự với hầu hết các câu trả lời phù hợp với mô hình khác.

Ideone .


Bạn không cần phải bắt ký tự đầu tiên trong 0trường hợp, giúp bạn tiết kiệm 5 byte.
Neil
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.