为简单的反向波兰符号编程语言优化编译器


24

描述

虚构编程语言(IPL)使用波兰语反向表示法。它具有以下命令:

  • 一世 -输入数字并将其推入堆栈
  • Ø堆栈的非破坏性输出顶部(数量保留在堆栈上)
  • d-丢弃栈顶
  • 整数 -将这个数字压入堆栈
  • +-* -从堆栈中弹出两个数字,执行相应的操作并将结果推回。IPL中没有划分。

IPL仅适用于整数,并用于简单计算。IPL程序写在一行上,并用空格分隔。空字符串是有效的IPL程序。

IPL计划:

i i + o 

输入两个数字,将它们加在一起并输出结果。

可以压入堆栈的输入数字和整数在[-999,999]范围内,但是输出可以是任意数字。如果您的语言不支持大数字,则可以。

输入/输出格式

您可以选择任何输入/输出格式,只要可以理解和读取/写入即可:字符串,列表,标记等。

任务

为您提供了一些IPL程序,您需要对其进行优化(减少长度):

i 12 + 3 + o d 2 3 + d

优化后将成为

i 15 + o

您不必保留堆栈状态,但是输入和输出的数量及其顺序应与原始程序和优化程序相匹配。

所以IPL程序:

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

优化后将成为

i -80 * o i 4 o i

要么

-80 i * o i 4 o i

(请注意,即使它们无关紧要,您也必须保存所有输入)。

测试用例应该没有硬编码,代码应该可以在任何任意IPL程序上工作,并且可以生成满足要求的最短IPL程序。

计分

默认的代码高尔夫评分。

更新:按照@Sanchises的建议,将得分改为纯代码高尔夫得分。

测试用例:

输入:

(empty string)

可能的输出:

(empty string)

输入:

i 4 * 2 + 3 * 6 - o

可能的输出:

i 12 * o

输入:

1 1 + o

可能的输出:

2 o

输入:

i 2 + 3 + o d 2 3 + d

可能的输出:

i 5 + o

输入:

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

可能的输出:

-80 i * o i 4 o i

输入:

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

可能的输出:

i i i i i d d d d o 

输入:

i i i 0 * * * o

可能的输出:

i i i 0 o

输入:

i i i 1 * * * o

可能的输出:

i i i * * o

输入:

i 222 + i 222 - + o

可能的输出:

i i + o

输入:

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

可能的输出:

i i i i i o 1 o

输入:

i 1 + 2 * 1 + o 

可能的输出:

i 2 * 3 + 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

可能的输出:

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
一个问题:您可以简化i i d oi o i(输入按顺序,输出按顺序)还是不简化?(一组输入输出应该有序)
Sanchises '18年

1
@Sanchises不,输入和输出应该有序。如果原始程序在输出任何经过优化的内容之前输入2个数字,则应该执行相同的操作。
АндрейЛомакин

1
欢迎来到PPCG!不错的第一个挑战!
路易斯·费利佩·德·耶稣·穆诺兹

6
审查队列来看,我认为这一挑战并不明确。如果这样做,请说明原因。
mbomb007 '18

2
@WW我认为OP意味着您不应该仅对问题中列出的测试用例进行硬编码。您必须支持任意输入。测试案例应该没有硬编码,代码应该可以 在任何IPL程序上工作
mbomb007 '18

Answers:


5

Wolfram语言(数学)733 728 690 564 516个 506 513 548字节

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]]&

在线尝试!

这是一个四步走法,(1)将“-”替换为“ -1 * +”,这样我们就不必处​​理减法;(2)稍微简化了命令列表,( 3)列出此命令列表的所有排列,并挑选出在解析(执行)时得出相同结果的命令;(4)将某些操作转换回后,将这些命令列表简化一些,并选择最短的命令减法。

该代码效率极低,因为它会遍历输入代码的所有排列列表。对于长输入代码,我不建议运行此代码。但在我阅读本文时,此挑战没有运行时或内存限制。

此代码在将所有“-”操作转换为带有翻转符号的“ +”操作之后执行优化步骤,并且仅在最后将代码转换回字符串时重新引入“-”运算符。例如,这意味着“ i -1 i * + o”已正确优化为“ ii-o”。

由于I / O格式要求非常宽松,因此该代码将代码作为列表返回,并以列表形式返回,其中符号“ +”,“-”,“ *”分别由p,m,t和标记表示。从字符串到字符串的转换是在TIO上提供的包装函数中完成的:

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

非高尔夫版本,包括字符串格式包装器,并最小化最终代码字符串的长度而不是令牌的数量,并包括更多的转换技巧:

(* 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               *)

感谢@redundancy捕获错误:解析器需要一个 Expand输出应用于输出,以处理分布式等效项。506→513

更新

现在还优化1 o 1 + o1 o 2 o。这是一个非常困难的案例,使代码变慢了很多。513→548


似乎这样给测试用例带来了错误i i 1 + i 1 + i 1 + i 1 + d d d d o
Grimmy

就像我说的@Grimy一样,该代码不会针对大问题运行,因为它经过详尽的组合搜索代码空间。您的错误是TIO上的内存不足错误,而不是由于我的代码。
罗马

@Grimy对于“ ii 1 + d o”,我的代码给出“ iid o”,我认为它已优化。对于“ ii 1 + i 1 + dd o”,它给出“ iii + d o”,其令牌数量与更明显的“ iiidd o”优化相同。我没有尝试更长的输入时间。
罗马

我相信输入i 2 * i 2 * + o应该产生优化的输出i i + 2 * o,但是这段代码返回了(未优化的)输入。
冗余

感谢@redundancy,它已修复,您的示例现在是其中包含的测试用例之一。
罗马
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.