评估三元运算符的表达式


29

考虑过字母表语法{ 0 1 ? :}由定义的产生式规则

小号→交通010 ?小号:小号┃ 1 ?小号:小号

给定从s生成的字符串,将其解析?:为右关联的表达式(例如,a?B?X:Y:c?d:e?f:gmeans a?(B?X:Y):(c?d:(e?f:g))),并使用以下语义对其进行求值:

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

如果结果为0,则输出一些固定值;如果输出为1,则输出不同的固定值。在答案中指定您选择的输出值(例如0/ 1False/ True)。

测试用例

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

规则

  • 您可能无法使用将字符串解释为某种编程语言中的代码并运行它的语言内置程序(例如JavaScript / Perl / Ruby / Python's eval)。
  • 就是说,您的代码实际上不必解析然后评估输入字符串。您可以采用任何方法获得相同的结果,并且不会违反先前的规则。
  • 您的程序将根据进行检查perl -le 'print eval<>'
  • 最短的代码(以字节为单位)获胜。

彻底改变字符串后,如何使用像eval这样的语言内置函数将字符串解释为$ my_language代码?
2013年

将字符串解释为$ some_other_language代码的内建函数呢?
Mego

@Adám,那是不允许的,对不起。
林恩

@Mego Hmm,那里有一个微不足道的作弊机会,所以我将规则扩展到涵盖所有此类内置函数。
林恩

1
根据Martin的测试用例,也许将语法定义为S → T | T ? S : S,会更简单T → 0 | 1,而无需谈论关联性?
彼得·泰勒

Answers:



17

视网膜,23字节

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

在线尝试!(第一行启用换行分隔的测试套件。)

说明

实际上,这很简单。通过重复(+)评估仅包含文字的三进制,可将输入减少为结果。为确保此操作正确关联,我们从右到左查找匹配项(r),仅替换找到的最后匹配项(-1=)。

正则表达式本身要么匹配0\?.:并删除它(仅将留在后面:),要么1\?.:.将其替换为之后的值?


如果正则表达式从右边开始,那么您是否应该处理1st匹配而不是-1st呢?
Leaky Nun

@LeakyNun不幸的是,我认为在应用限制之前我会取消比赛。
Martin Ender

10

Haskell中,106 101 100 90 83个字节

这在很大程度上依赖于模式Haskell的模式匹配功能。首先,我们反转字符串,以便可以仅搜索第一次出现的b:a?x(通常会读作x?a:b)并将其替换为它的值。这将自动提供正确的关联性。在这里,我们利用x:xs模式。该功能f正在执行此操作。然后,我们基本上f一遍又一遍地将其应用于输出,直到剩下单个数字(0或1)为止。

感谢@Lynn 12个字节!

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字节

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

输出是\xff用于0\x001。精明的实现必须允许转到起始单元格的左侧。

这使用了与xsot的Python 答案基本相同的方法,但是与我最初的82字节提交相比,分支可能更难遵循:

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

(对于此解决方案,输出为\xfefor 0\xfffor 1,当输入以换行符结尾时,可实现更大的兼容性。)

如果您不愿意分析xsot的解决方案,那么您的想法就是这样:从左到右。如果看到,1?则将其贪婪地丢弃。如果看到的0?话,则丢弃该位置和相应位置之间的所有内容:。当?不显示为第二个字符时,请停止循环并打印剩余字符串的第一个字符。

因此,该82字节的解决方案实际上非常接近地反映了该方案。内循环处理0?,就像xsot的内循环一样。为了不进入任何输入字符就进入主循环要特别注意。也就是说,我们要检查第二个字符?在主循环结束时是否只是一次,而不是在进入主循环之前在开始时是否仅一次。

63字节的解决方案实质上将内部和外部循环组合为一个,我怀疑考虑到这些循环之间的相似性,这是可能的。主循环中的内存布局可以描述为:

[s] d c

其中[x]表示当前单元格- s以一个虚拟非零值开头,该值表示我们仍在循环,并立即用输入字符(01)覆盖。d如果我们位于a的中间0?,则该单元格保持(负)深度,否则为0。该c会是两种?:或换行或EOF。

更新s和之后c,我们将0?通过相应地更新d并调整指针来处理这种情况,否则我们将使用当前值c作为d下一个迭代中的值,或者如果完成则停止。


7

Python 2,76 74 73 72字节

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

用输入作为字符串文字可以避免raw_

输出为01后跟<built-in function id>


1
哈哈,我刚刚阅读了您对b lang的答案,并且打算发布几乎相同的答案!这是另外一个优化方法:3>>int(c)
xsot

关心解释这是如何工作的吗?看起来真的很整洁
WorldSEnder

@WorldSEnder我认为这是很难解决的类型,但是一旦您看到它便易于理解。它像其他求解程序一样,向后遍历字符串并重复处理最右边的条件。
米奇·施瓦茨

那个`id`把戏……!做得好:)
林恩

5

Python 2,89个字节

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

输入被当作字符串文字。


5

尘垢34 31字节

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

打印1真实的输入和0在线尝试! 不幸的是,最后一个测试用例在TIO上的内存不足。

说明

右关联本质上意味着在a?b:ca始终为01,从未更长的表达。我将以递归方式定义一个匹配真实表达式的模式,并对照该表达式检查输入。如果所有s都被选中,也不必检查每个元素:是否真的是a :输入中s和s的数目相等,并且如果某些被错误地分类为a ,则对应的将不匹配,并且Grime的匹配引擎将回退。:??:?::

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字节

编辑:感谢@Zgarb 3个字节和@nimi 4个字节!

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

这是一种递归方法,在某种程度上滥用了“某些固定值”-输出规则。编辑:摆脱元组不仅节省8个字节,而且还产生更好的输出:"0""1"

非高尔夫版本:

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)

它是如何工作的?
eval函数遍历表达式的隐式树

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

并返回形式为的元组(result, rest)
第一个模式(x:'?':r1)匹配x'1'r1"0?0:1:0?1:0"。递归地应用evalr1求值的子表达式0?0:1并返回(0,":0?1:0")。将其匹配到模式将(a,':':r2)产生a=0r2=0?1:0。该子公式还被递归求值,因此b='0'r3=""。检查xis '1'或,'0'然后返回(a, r3)or (b, r3)


1
不错的方法!将x>'0'在地方工作x=='1'
Zgarb '16

谢谢,我在处理字符时没有想到这一点。
Laikoni '16

1
你也可以更换':'_
Zgarb

是的,然后代码甚至可以用于任意定界符,而不是just :。再次感谢!
Laikoni

1
真好!你可以替换if .. then .. else使用last$e s:[a:tail(e s)|x>'0']
nimi

3

JavaScript(ES6),53个字节

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

返回01用于有效输入;因无效输入而挂起。说明:因为在JavaScript中反转字符串很尴尬,所以我的第一个71字节尝试是通过对a使用负数超前工作来进行的?,否则会干扰关联性:

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

由于时间太长,我想知道是否可以通过将决策纳入正则表达式来改善问题。事实证明,这并不是立即成功的,因为它也占用了71个字节:

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

然后我想到0?0:0?1:并且总是无人问津,而不必担心关联性。这为我节省了将近25%。


您位于顶部的代码块丢失了f=。我尚未检查您的字节数是否已考虑在内。
帕特里克·罗伯茨

@PatrickRoberts我一直在这样做,因为我从日志中复制日志,该日志仅显示赋值的结果(当然,这对于非递归函数就足够了)。
尼尔

@Neil您可以从日志输入复制而不是从输出复制
ASCII码,仅ASCII

@MarsUltor日志输入包含提示,然后我必须记住要排除该提示。对于非递归函数,这是一个笨拙的额外步骤,这就是为什么我默认情况下从输出中进行复制的原因。
尼尔

3

Perl,32 + 1(-p标志)= 33个字节

完全 感谢@Mitch Swartch,因为他的解决方案比我的解决方案短14个字节!
也要感谢@Neil,他建议的解决方案比Mitch长1个字节。

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

需要-p标志,以及-M5.010-E运行。例如 :

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"

说明:基本上,它减少了a?b:c(从末尾开始以确保没有?跟随)到bc取决于,的真实性的块a,直到字符串仅包含1或为止0


是否-没有对你的分数算?嗯 有趣的...好答案!
2016年

@MayorMonty对于1衬里,您可以在命令行上使用调用它,perl -e '<code>'因此p仅添加1个字节perl -pe '<code>'
尼尔

@Neil Ahh,这很有道理
MayyMonty '16

实际上,您不必反转字符串,您可以对a进行负向超前查找?,因此我可以将其缩减为34个字节。
尼尔

这是32 + 1:s/.*\K(1\?(.)..|0\?..)/\2/&&redo
米奇·施瓦茨

2

Python 3,93 69字节

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

输入是作为字符列表的字符串,输出是"0""1"

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

非高尔夫版本:

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

再次尝试,但具有更多的字节:

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

您的答案可能是一个功能-您可以删除第二行。
林恩

显然,这未经测试,因为它没有通过测试用例,并且未发布的版本给出了运行时错误。您的基本想法是好的。有了一些调整,我得到68 Python 2和69在Python 3
米奇施瓦茨

1
好吧,我认为给您答案比将其编辑成我自己的答案更有意义(因为在看到您的答案之前我并没有考虑过这种方法),或者在您的答案有问题时坐在那里等待。这是我提到的68 def f(s):x=s.pop(0);return[]<s<s.pop(0)>'>'and(f(s),f(s))[x<'1']or x,对于与您的编辑距离较小的Python 3,有def f(s):z=s.pop;r=z(0);return s and':'<z(0)and(f(s),f(s))[r<'1']or r
米奇·施瓦兹

感谢@MitchSchwartz,从右到左而不是从左到右分别解析
WorldSEnder

1
否则,左而不是右,~~~
WorldSEnder 16'Aug

1

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

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

您可能可以使用@MitchSchwartz在他的评论中的技巧来减少这种情况,尽管您可能必须使用(.*)并添加一个额外的替换术语。
尼尔

@Neil,您可能是正确的,但我不知道如何使其工作。
莱利

我在聊天中写了它,因为注释中的格式可能会引起混淆:chat.stackexchange.com/transcript/message/31709640#31709640
Mitch Schwartz

@MitchSchwartz嘿,空白标签有效吗?但是我认为你\3不是故意的\2。另外,您也可以加入;以获取相关信息:;s/(.*)(1\?(.):.|0\?.:)/\1\3/;t
尼尔

@Neil,\3所以这意味着我不小心复制了以前的版本。我知道分号。但是,,为什么要使用分号。
米奇·施瓦兹

0

Bash + GNU实用程序,42

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

与大多数其他模式匹配答案相似的想法。

伊迪奥


在这种0情况下,您无需捕获第一个字符,这样可以节省5个字节。
尼尔
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.