将表达式转换为Panfix表示法


19

我正在浏览esolangs,偶然发现了这种语言:https : //github.com/catseye/Quylthulg

关于这种语言的一个有趣的事情是,它不使用前缀,后缀或中缀,它使用 这三个名称,将其称为“ panfix”表示法。

这是一个例子。为了1+2在panfix中表示普通的中缀,它变为:+1+2+。注意操作符在操作数之前,之间和之后的状态。另一个例子是(1+2)*3。这变成了*+1+2+*3*。再次注意*关于操作数的所有三个地方情况如何+1+2+和的3

挑战

正如您可能已经猜到的那样,此挑战中的任务是将表达式从infix转换为panfix。

一些澄清:

  • 您只需要处理四个基本操作: +-*/
  • 您将不必处理那些一元版本,只需二进制
  • 你必须处理括号
  • 假设*/那时的正常优先级规则+-和所有左关联性。
  • 这些数字将是非负整数
  • 您可以选择在输入和输出中都留一个空格

测试用例

1+2  ->  +1+2+
1+2+3  ->  ++1+2++3+
(1+2)*3  ->  *+1+2+*3*
10/2*5  ->  */10/2/*5*
(5+3)*((9+18)/4-1)  ->  *+5+3+*-/+9+18+/4/-1-*

这是,因此以字节为单位的最短代码胜出!

Answers:


3

JavaScript(ES6),160字节

f=(s,t=s.replace(/[*-/]/g,"'$&'"),u=t.replace(/^(.*?)([*-9]+)'([*/])'([*-9]+)|([*-9]+)'([+-])'([*-9]+)|\(([*-9]+)\)/,"$1$3$2$3$4$3$6$5$6$7$6$8"))=>t==u?t:f(s,u)

通过引用所有运算符(在运算符之前为其提供字符代码*),然后查找可用的'*''/'操作'+''-'操作或()s,然后用其panfix表示法替换第一个,来进行工作。例:

(5+3)*((9+18)/4-1)
(5'+'3)'*'((9'+'18)'/'4'-'1)
(+5+3+)'*'((9'+'18)'/'4'-'1)
+5+3+'*'((9'+'18)'/'4'-'1)
+5+3+'*'((+9+18+)'/'4'-'1)
+5+3+'*'(+9+18+'/'4'-'1)
+5+3+'*'(/+9+18+/4/'-'1)
+5+3+'*'(-/+9+18+/4/-1-)
+5+3+'*'-/+9+18+/4/-1-
*+5+3+*-/+9+18+/4/-1-*

3

的JavaScript(ES6),285 282 281 267 251 243个 241 238 234 232 231字节

多亏Neil〜15个字节。

f=(I,E=I.match(/\d+|./g),i=0)=>(J=T=>T.map?T.map(J).join``:T)((R=(H,l=(P=_=>(t=E[i++])<")"?R(0):t)(),C,F)=>{for(;(C=P())>")"&&(q=C>"*"&&C<"/")*H-1;)F=q+H?l=[C,l,C,P(),C]:F?l[3]=[C,l[3],C,R(1),C]:l=R(1,l,i--)
i-=C>")"
return l})(0))

在JavaScript中,这比在Mathematica中要难一些。从本质上讲,这是一个过度专业化的操作员优先级解析器

在无效输入上导致堆栈溢出。

演示版

不打高尔夫球

convert = input => {
  tokens = input.match(/\d+|./g);
  i = 0;
  parse_token = () => (token = tokens[i++]) == "(" ? parse_tree(false) : token;
  parse_tree = (mul_div_mode, left = parse_token()) => {
    while ((oper = parse_token()) != ")" && !((is_plus_minus = oper == "+" || oper == "-") && mul_div_mode)) {
      if (is_plus_minus || mul_div_mode)
        left = [oper, left, oper, parse_token(), oper];
      else if (non_first)
        left[3] = [oper, left[3], oper, parse_tree(true), oper];
      else
        left = parse_tree(true, left, i--);
      non_first = true;
    }
    if (oper != ")")
      i--;
    return left;
  };
  format_tree = tree => tree.map ? tree.map(format_tree).join("") : tree;
  return format_tree(parse_tree(false));
}

S.split``应该是[...S],尽管实际上可能有助于预先匹配/\d+|./g并代替它工作。
尼尔

@尼尔谢谢。我会调查的。
PurkkaKoodari'7

2

Mathematica, 203 195字节

这可能效率不高,但似乎可以完成任务。

Function[f,ReleaseHold[(Inactivate@f/._[Plus][a_,b_/;b<0]:>a~"-"~-b//Activate@*Hold)//.a_/b_:>a~"/"~b/.{a_Integer:>ToString@a,Plus:>"+",Times:>"*"}]//.a_String~b_~c_String:>b<>a<>b<>c<>b,HoldAll]

这是一个匿名函数,它使用实际表达式并返回带有panfix表示法的字符串。Mathematica在解析时(而不是评估时)整理运算符的优先级,因此嵌套应自动正确。至少测试用例能按预期工作。

说明:将整个表达式解释为树很容易,如下所示:

树

在此阶段,运算符(不是叶子的每个节点)不再是运算符,它们实际上已转换为字符串,例如"+"。整数也被转换为字符串。然后,重复的替换规则会将恰好具有两个叶子的每个节点转换为panfix parent-leaf1-parent-leaf2-parent。经过一些迭代后,树减少为单个字符串。

字节数的主要损失是Mathematica解释

5 - 4 -> 5 + (-4)
9 / 3 -> 9 * (3^(-1))

而且这也在解析时发生。

略微打高尔夫球,因为该模式a_/b_也被解释为a_ * (b_)^(-1)。在其他地方也有一些小的优化。


1

Prolog,87个字节

x(T)-->{T=..[O,A,B]}->[O],x(A),[O],x(B),[O];[T].
p:-read(T),x(T,L,[]),maplist(write,L).

这是一个函数(主要是因为编写完整的程序在Prolog中具有噩梦般的水平;通常,即使编译程序,运行时它也会产生REPL)p。它从stdin接受输入,在stdout接受输出。注意,您需要在输入中附加一个句点,这是Prolog输入例程工作方式的不幸结果(它们在输入中使用句点的方式与其他语言使用换行符的方式几乎相同);可能会或可能不会取消答案的资格。

说明

在Prolog中,算术运算符通常被解释为元组构造函数。但是,它们遵循与其所基于的实际算术运算符相同的优先级规则。您可以使用infix表示法形成元组,并且绑定+-绑定的关系不如*and 紧密/,并且优先级在组中从左到右。这正是这个问题所要的。因此,我们可以从输入中读取整个嵌套元组,并且它已经具有正确的结构。就是p那样

接下来,我们需要将其转换为panfix表示法。x将输入转化为建设者和整数panfixed列表,并且可以理解为几乎直接的英语句子:“ xT是:如果T是构造一个元组O和参数AB,那么OxAOxBO的,否则T”。最后,我们只需要打印不带任何分隔符的列表(即maplist用于调用write列表的每个元素)。

我使用SWI-Prolog对此进行了测试,因为我的GNU Prolog版本还maplist没有(显然它已经添加到了较新的版本中),但是通常在Prolog实现之间应该可以移植。

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.