Tối ưu hóa trình biên dịch cho ngôn ngữ lập trình đảo ngược tiếng Ba Lan đơn giản


24

Sự miêu tả

Ngôn ngữ lập trình tưởng tượng (IPL) sử dụng ký hiệu đảo ngược tiếng Ba Lan. Nó có các lệnh sau:

  • i - số đầu vào và đẩy nó vào ngăn xếp
  • o - đỉnh đầu ra không phá hủy của ngăn xếp (số vẫn ở trên ngăn xếp)
  • d - loại bỏ đỉnh ngăn xếp
  • số nguyên - đẩy số này vào ngăn xếp
  • + - * - bật hai số từ ngăn xếp, thực hiện thao tác tương ứng và đẩy lùi kết quả. Không có sự phân chia trong IPL.

IPL chỉ hoạt động với số nguyên và được sử dụng cho các phép tính đơn giản. Một chương trình IPL được viết trên một dòng và cách nhau bởi khoảng trắng. Chuỗi rỗng là một chương trình IPL hợp lệ.

Chương trình IPL:

i i + o 

Nhập hai số, cộng chúng lại với nhau và đưa ra kết quả.

Số đầu vào và số nguyên có thể được đẩy lên ngăn xếp nằm trong phạm vi [-999, 999], tuy nhiên đầu ra có thể là bất kỳ. Nếu ngôn ngữ của bạn không hỗ trợ số lượng lớn thì cũng không sao.

Định dạng đầu vào / đầu ra

Bạn có thể chọn bất kỳ định dạng đầu vào / đầu ra miễn là rõ ràng để hiểu và đọc / ghi: chuỗi, danh sách, mã thông báo, v.v.

Bài tập

Bạn được cung cấp một số chương trình IPL, bạn cần tối ưu hóa nó (giảm độ dài):

i 12 + 3 + o d 2 3 + d

Sau khi tối ưu hóa sẽ trở thành

i 15 + o

Bạn không phải duy trì trạng thái ngăn xếp, nhưng số lượng đầu vào và đầu ra và thứ tự của chúng phải phù hợp với chương trình gốc và tối ưu hóa.

Vì vậy, chương trình IPL:

-40 i * 2 * o i + 3 1 + o i 2 *

Sau khi tối ưu hóa sẽ trở thành

i -80 * o i 4 o i

hoặc là

-80 i * o i 4 o i

(lưu ý rằng bạn phải lưu tất cả các đầu vào, ngay cả khi chúng không liên quan).

Không nên có mã hóa cứng cho các trường hợp thử nghiệm, mã phải hoạt động trên bất kỳ chương trình IPL tùy ý nào và tạo ra chương trình IPL ngắn nhất có thể đáp ứng các yêu cầu.

Chấm điểm

Mã điểm golf mặc định.

CẬP NHẬT: thay đổi cách tính điểm thành ghi điểm golf thuần, theo đề xuất của @Sanchises.

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

Đầu vào:

(empty string)

Sản lượng có thể:

(empty string)

Đầu vào:

i 4 * 2 + 3 * 6 - o

Sản lượng có thể:

i 12 * o

Đầu vào:

1 1 + o

Sản lượng có thể:

2 o

Đầu vào:

i 2 + 3 + o d 2 3 + d

Sản lượng có thể:

i 5 + o

Đầu vào:

-40 i * 2 * o i + 3 1 + o i 2 *

Sản lượng có thể:

-80 i * o i 4 o i

Đầu vào:

i i 1 + i 1 + i 1 + i 1 + d d d d o 

Sản lượng có thể:

i i i i i d d d d o 

Đầu vào:

i i i 0 * * * o

Sản lượng có thể:

i i i 0 o

Đầu vào:

i i i 1 * * * o

Sản lượng có thể:

i i i * * o

Đầu vào:

i 222 + i 222 - + o

Sản lượng có thể:

i i + o

Đầu vào:

i 2 + 3 * 2 + 3 * 2 + 3 * i * d i 2 + 3 * i + d i o 2 + 2 - 0 * 1 o

Sản lượng có thể:

i i i i i o 1 o

Đầu vào:

i 1 + 2 * 1 + o 

Sản lượng có thể:

i 2 * 3 + o

Đầu vào:

1 1 + o i 2 + 3 + o d 2 3 + d 4 i * 2 * o i + 3 1 + o i 2 * i i 1 + i 1 + i 1 + i 1 + d d d d o i i i 0 * * * o i i i 1 * * * o i 2 + i 2 - + o i 2 + 3 * 2 + 3 * 2 + 3 * i * d i 2 + 3 * i + d i o 2 + 2 - 0 * 1 o

Sản lượng có thể:

2 o i 5 + o 8 i * o i 4 o i i i i i i d d d d o i i i 0 o i i i * * * o i i + o i i i i i o 1 o

1
Một câu hỏi: bạn có thể đơn giản hóa i i d ođến i o i(đầu vào là theo thứ tự và đầu ra là theo thứ tự) hay nên bạn không đơn giản hóa nó? (bộ đầu vào đầu ra phải theo thứ tự)
Sanchise

1
@Sanchise không, đầu vào và đầu ra nên theo thứ tự. Nếu chương trình gốc nhập 2 số trước khi xuất bất cứ thứ gì được tối ưu hóa thì cũng nên làm như vậy.
Tiếng Pháp là

1
Chào mừng đến với PPCG! Thử thách đầu tiên thật tuyệt!
Luis felipe De jesus Munoz

6
Từ hàng đợi đánh giá , tôi không nghĩ rằng thách thức này không rõ ràng. Nếu bạn làm, hãy bình luận về lý do tại sao.
mbomb007

2
@WW Tôi nghĩ rằng OP có nghĩa là bạn không nên chỉ mã hóa cứng các trường hợp kiểm tra được liệt kê trong câu hỏi. Bạn phải hỗ trợ đầu vào tùy ý. Không nên có mã hóa cứng cho các trường hợp thử nghiệm, mã phải hoạt động trên bất kỳ chương trình IPL tùy ý nào
mbomb007

Câu trả lời:


5

Ngôn ngữ Wolfram (Mathicala) , 733 728 690 564 516 506 513 548 byte

j=Integer;f=Flatten;s=SequenceReplace;A=FixedPoint[f@s[#,{{x_j,p,y_j,t}->{y,t,x*y,p},{x_j,y_j,p}->x+y,{x_j,y_j,t}->x*y,{x_j,p,y_j,p}->{x+y,p},{x_j,t,y_j,t}->{x*y,t},{0,p}|{1,t}->{},{0,t}->{d,0}}]//.{a___,Except[i|o]}->{a}&,#]&;B=Expand@Check[f@FoldPairList[f/@Switch[#2,i,{{i},{#,i@c++}},o,{{Last@#},#},d,{{},Most@#},p,{{},{#[[;;-3]],Tr@#[[-2;;]]}},t,{{},{#[[;;-3]],#[[-2]]*Last@#}},_,{{},{##}}]&,c=0;{},#],x]&;F=MinimalBy[w=A@f[#/.m->{-1,t,p}];z=B@w;s[#,{-1,t,p}->m]&/@A/@Select[Permutations@Join[w,Cases[z /.i@_->i,_j,∞]],B@#==z&],Length][[1]]&

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

Đây là một chuyến tham quan bốn bước mà (1) thay thế "-" bằng "-1 * +" để chúng ta không phải xử lý các phép trừ, (2) đơn giản hóa danh sách các lệnh một chút, ( 3) lập danh sách tất cả các hoán vị của danh sách các lệnh này và chọn ra các lệnh cho kết quả tương tự khi được phân tích cú pháp (thực thi) và (4) đơn giản hóa các danh sách lệnh này một chút và chọn ngắn nhất, sau khi chuyển đổi một số thao tác trở lại phép trừ.

Mã này cực kỳ kém hiệu quả vì nó đi qua danh sách tất cả các hoán vị của mã đầu vào. Đối với mã đầu vào dài, tôi không khuyên bạn nên chạy mã này; nhưng khi tôi đọc nó, không có giới hạn thời gian chạy hoặc bộ nhớ trong thử thách này.

Mã này thực hiện bước tối ưu hóa sau khi chuyển đổi tất cả các hoạt động "-" thành các hoạt động "+" với các dấu hiệu được lật và cuối cùng chỉ giới thiệu lại toán tử "-" khi chuyển đổi mã trở lại chuỗi. Điều này ngụ ý ví dụ rằng "i -1 i * + o" được tối ưu hóa chính xác thành "ii - o".

Vì yêu cầu định dạng i / o khá lỏng lẻo, mã này nhận và trả về mã dưới dạng danh sách, trong đó các ký hiệu "+", "-", "*" được biểu thị bằng p, m, t, mã thông báo tương ứng. Việc chuyển đổi từ và sang chuỗi được thực hiện trong chức năng trình bao bọc được đưa ra trên TIO:

G[S_] := StringReplace[{"p" -> "+", "m" -> "-", "t" -> "*"}]@StringRiffle@
         Quiet@F@
         ToExpression[StringSplit[S] /. {"+" -> p, "-" -> m, "*" -> t}]

Phiên bản không được đánh gôn, bao gồm trình bao bọc định dạng chuỗi và giảm thiểu độ dài chuỗi mã cuối cùng thay vì số lượng mã thông báo và bao gồm một số chi tiết chuyển đổi khác:

(* convert code string to list of operators *)
inputfilter[s_] := ToExpression[Flatten[StringSplit[s] /.
  {"i" -> i, "o" -> o, "d" -> d, "+" -> p, "-" -> {-1, t, p}, "*" -> t}]]

(* convert list of operators to code string *)
outputfilter[s_] := StringReplace[StringRiffle@Flatten@SequenceReplace[s,
  {{-1, t, p} -> m,                         (* convert "-1 t p" back to "-"             *)
   {x_ /; x < 0, p} -> {-x, m},             (* convert "y x +" to "y -x -" when x<0     *)
   {x_ /; x < 0, t, p} -> {-x, t, m}}],     (* convert "y x * +" to "y -x * -" when x<0 *)
  {"m" -> "-", "p" -> "+", "t" -> "*"}]     (* backsubstitution of symbols              *)

(* simplify a list of operators somewhat *)
simplifier[s_] := FixedPoint[Flatten@SequenceReplace[#,
  {{x_Integer, p, y_Integer, t} -> {y, t, x*y, p},  (*  "x + y *" -> "y * (xy) +"       *)
   {x_Integer, y_Integer, p} -> x + y,              (*  "x y +" -> "(x+y)"              *)
   {x_Integer, y_Integer, t} -> x*y,                (*  "x y *" -> "(xy)"               *)
   {x_Integer, p, y_Integer, p} -> {x + y, p},      (*  "x + y +" -> "(x+y) +"          *)
   {x_Integer, t, y_Integer, t} -> {x*y, t},        (*  "x * y *" -> "(xy) *            *)
   {0, p} | {1, t} -> {},                           (*  "0 +" and "1 *" are deleted     *)
   {x_Integer, i, p} -> {i, x, p},                  (*  "x i +" -> "i x +"              *)
   {x_Integer, i, t} -> {i, x, t},                  (*  "x i *" -> "i x *"              *)
   {0, t} -> {d, 0}}] //.                           (*  "0 *" -> "d 0"                  *)
  {a___, Except[i | o]} -> {a} &, s]                (* delete trailing useless code     *)

(* execute a list of operators and return the list of generated outputs *)
parse[s_] := Expand@Quiet@Check[Flatten@FoldPairList[  (* stack faults are caught here     *)
  Function[{stack, command},                        (* function called for every command*)
    Flatten /@ Switch[command,                      (* code interpretation:             *)
    i, {{i}, {stack, i[inputcounter++]}},           (* output "i" and add input to stack*)
    o, {{stack[[-1]]}, stack},                      (* output top of stack              *)
    d, {{}, Most[stack]},                           (* delete top of stack              *)
    p, {{}, {stack[[;; -3]], stack[[-2]] + stack[[-1]]}},  (* add two stack elements    *)
    t, {{}, {stack[[;; -3]], stack[[-2]]*stack[[-1]]}},    (* multiply two stack elements*)
    _, {{}, {stack, command}}]],                    (* put number onto stack            *)
    inputcounter = 0; {},                           (* start with zero input counter and empty stack*)
    s],                                             (* loop over code list              *)
  x]                                                (* return "x" if an error occurred  *)

(* the main function that takes a code string and returns an optimized code string *)
F[s_] := Module[{w, q},
  w = simplifier@inputfilter@s;      (* convert input to useful form *)
  q = parse[w];                      (* execute input code *)
  MinimalBy[
    outputfilter@*simplifier /@      (* simplify and stringify selected codes          *)
      Select[Permutations[w],        (* all permutations of code list                  *)
             parse[#] == q &],       (* select only those that give the correct output *)
    StringLength] // Union]          (* pick shortest solution by length               *)

Nhờ @redundancy để bắt lỗi: Trình phân tích cú pháp cần Expandáp dụng cho đầu ra để xử lý tương đương phân phối. 506 → 513

cập nhật

Bây giờ cũng tối ưu hóa 1 o 1 + ođể 1 o 2 o. Đây là một trường hợp khó khăn đáng ngạc nhiên và làm cho mã chậm hơn nhiều. 513 → 548


Có vẻ như điều này cho một lỗi trên trường hợp thử nghiệm i i 1 + i 1 + i 1 + i 1 + d d d d o.
Grimmy

@Grimy như tôi đã nói, mã này không chạy cho các vấn đề lớn vì nó trải qua quá trình tìm kiếm kết hợp không gian mã đầy đủ. Lỗi của bạn là lỗi hết bộ nhớ trên TIO và không phải do mã của tôi.
La Mã

@Grimy cho "ii 1 + d o" mã của tôi cho "iid o", mà tôi cho là tối ưu hóa. Đối với "ii 1 + i 1 + dd o", nó cung cấp "iii + d o", có cùng số lượng mã thông báo như tối ưu hóa "iiidd o" rõ ràng hơn. Tôi đã không thử đầu vào lâu hơn.
La Mã

Tôi tin rằng đầu vào i 2 * i 2 * + osẽ tạo ra đầu ra được tối ưu hóa i i + 2 * o, nhưng mã này trả về đầu vào (không tối ưu hóa).
dự phòng

Cảm ơn @redundancy, nó đã được sửa và ví dụ của bạn bây giờ là một trong những trường hợp thử nghiệm được bao gồm.
La Mã
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.