単純な逆ポーランド記法プログラミング言語用にコンパイラを最適化する


24

説明

仮想プログラミング言語(IPL)はポーランド語逆表記法を使用します。次のコマンドがあります。

  • i-番号を入力し、スタックにプッシュします
  • o-スタックの非破壊出力トップ(数値はスタックにとどまります)
  • d-スタックの先頭を破棄
  • 整数 -この数値をスタックにプッシュします
  • +-* -スタックから2つの数値をポップし、対応する操作を実行して結果をプッシュバックします。IPLには区分はありません。

IPLは整数でのみ機能し、単純な計算に使用されます。IPLプログラムは1行で記述され、スペースで区切られます。空の文字列は有効なIPLプログラムです。

IPLプログラム:

i i + o 

2つの数値を入力し、それらを加算して結果を出力します。

スタックにプッシュできる入力番号と整数は[-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

1
@Sanchisesいいえ、入力と出力は順番に並んでいる必要があります。元のプログラムが出力する前に2つの数字を入力した場合、最適化されたものはすべて同じことをするはずです。
АндрейЛомакин

1
PPCGへようこそ!ナイスファーストチャレンジ!
ルイスフェリペデジェススムニョス

6
レビューのキューから、私はこの課題が不明確だとは思わない。もしそうなら、その理由についてコメントしてください。
mbomb007

2
@WW OPは、質問にリストされているテストケースのみをハードコーディングしないことを意味すると思います。任意の入力をサポートする必要があります。テストケースのハードコーディングはないはずです。コードは 任意のIPLプログラムで
mbomb007

回答:


5

Wolfram Language(Mathematica)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]]&

オンラインでお試しください!

これは4段階のツアーデフォースであり、(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です。
グリムミー

@Grimyが言ったように、このコードは大きな問題に対しては実行されません。コードスペースの徹底的な組み合わせ検索を実行するためです。あなたのエラーは、TIOのメモリ不足障害であり、私のコードによるものではありません。
ローマ

「ii 1 + d o」の@Grimyは、コードで「iid o」を生成しますが、最適化されていると考えています。「ii 1 + i 1 + dd o」の場合、「iii + d o」となります。これは、より明白な「iiidd o」最適化と同じ数のトークンを持ちます。長い入力は試していません。
ローマ

入力i 2 * i 2 * + oは最適化された出力を生成するはずi i + 2 * oですが、このコードは(最適化されていない)入力を返します。
冗長性

@redundancyのおかげで、修正され、あなたの例は含まれているテストケースの1つになりました。
ローマ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.