解决数学问题符号


14

想象一下,我有无数个作业问题(!),每个问题都有一个整数。

数学问题符号是一种使用问题说明符描述问题子集的符号。

MPN表达式可以包含以下几项:

  • 单个值。这表示一个包含数字的集合:99 -> {99}
  • 一个简单的范围。这表示包含从范围的开始到结束的所有数字的集合10~13 -> {10, 11, 12, 13}。如果左侧或右侧丢失,那么他们被认为是负无穷大或无穷大分别为:~10 -> {x|x ≤ 10}; ~ -> ℤ
  • 一个MPN表达式,后跟“跳过”和另一个MPN表达式。这代表了两组的区别:10~20 skip 12~14 -> {10, 11, 15, 16, 17, 18, 19, 20}
  • 两个MPN表达式,以逗号分隔。这表示两个集合的并集:1,8~9,15~17 -> {1,8,9,15,16,17}

“跳过”运算符的绑定比逗号运算符更紧密,因此16,110~112 skip 16 -> {16,110,111,112}(16个不包括在集合中{110,111,112}中不包括16,因此排除16无关紧要。)

您也可以将表达式放在括号中以消除歧义: 1~9 skip (2~8 skip (3~7 skip (4~6 skip 5))) -> {1,3,5,7,9}

这是语法:

<expr>  ::= "(" <expr> ")"
         || <number>
         || [<number>] "~" [<number>]
         || <expr> "skip" <expr>
         || <expr> "," <expr>

您的任务是编写一个接受两个输入的程序:

  • MPN表达式
  • 一个号码

并根据该问题是否在MPN表达式描述的集合中,输出一些真实或错误值。

技术指标

  • 您可以假定第一个输入是格式正确的MPN表达式(即,它与上述语法匹配)
  • MPN表达式中的数字始终是整数。它们可以是负数或零,但永远不会有小数部分。
  • 这是,因此最短的有效提交(以字节为单位)获胜。
  • 如果需要,可以为~和使用不同的字符,

测试用例

10~20             14 -> True
10~20             20 -> True
10~20 skip 14~18  17 -> False
~ skip 6          8  -> True
16,17 skip 16     16 -> True
(16,17) skip 16   16 -> False
~10,5~            8  -> True
~10,5~            4  -> True
6 skip 6,~        6  -> True

是否有可能使用其它字符来表示运营商使用#代替〜的。比如?
rahnema1

1
@ rahnema1支持~,,但不支持skip
Esolanging Fruit'3

3
为什么〜10,5〜为4是假的?因为那是-infinity与10的并集和5至infinity的并集,所以其中的第一个包含4
ev3commander

@ ev3commander编辑。我总是弄乱我的测试用例。我敢打赌,如果不添加挑战,挑战会更加清晰:P
Esolanging Fruit

1
@ Challenger5我添加了一个测试用例6 skip 6,~相信我已经正确解释了。到目前为止,其他两个答案都不满意(再次假设我的解释正确)。如果我误解了,请更正并弄清楚,但据我所知,它应该匹配任何内容(这是不匹配的集合的并集与匹配所有内容的集合的并集)。这些是我之前谈到的案例,我认为这些案例在测试我们的解决方案时会有所帮助。
briantist

Answers:


3

PowerShell189195字节

param($m,$q)('$m',"'(\d*)~(\d*)','($q-ge(`$1)-and$q-le(`$2))'","'\(\)',$q","'((?<=,|skip )\d+|\d+(?=,| skip))','($q-eq`$1)'","'skip','-and!'"-join'-replace'|iex|% Sp* ','|%{"($_)"})-join'-or'|iex

说明

我很早就意识到,无限性使生成数组和测试值变得站不住脚。

我查看了范围,但在.Net中,它们没有所需的范围(范围的长度限制为带符号的(32位)整数,因此即使可以将范围限制为带符号的32位int ,我将无法处理所有范围。

因此,我开始只是从开始和结束的角度考虑这个问题,最终进行了一系列布尔测试,并开始创建一堆正则表达式替换,以将MPN转换为PowerShell可以理解的布尔表达式。

我基本上将其分解为一些规则:

  • 范围首先比较容易使用,因为它们不能在任何一端作为表达式,但是开放式是在短期内实现的一种痛苦。前提2~8就像是说n >=2 && n <=8,但是当其中一个末端缺失时,就忽略了&&缺失的一面。当两者都丢失时,我本来只是将其替换为$true。我最终所做的只是根本没有测试缺失的一面,但是我确保将每个数字都包裹在中()
  • 然后进行直接替换,将空括号替换()为输入值。因此,如果MPN ~8的输入值为55,则将生成第一次替换(55-ge()-and55-le(8)),然后进行第二次替换(55-ge55-and55-le(8)),实际上使范围的该部分无效。
  • 接下来,我不得不处理MPN中的个人号码,但必须注意不要弄乱我以前插入的号码。它实际上只是逗号,分隔列表中的数字,以及a之前或之后的单个数字skip,因此我使用了很长的麻烦。
  • skip基本上与相同,-and -not因此我直接替换skip-and!!用作速记-not)。
  • 下一个棘手的事情是,其余逗号的优先级较低。我最初只是将它们替换为,-or但没有考虑后续表达式,因此16,17 skip 16生成了类似的代码($n-eq16)-or($n-eq17) -and! ($n-eq16)。它需要括号,但是直接替换似乎不可行。由于除逗号以外的所有其他内容都被替换,并且它们的优先级最低,因此我将整个生成的字符串拆分为其余的逗号,然后将每个元素括在括号中,然后用重新加入-or

最终,将生成的代码通过管道传递到Invoke-Expressioniex)中以执行,然后得到布尔结果(您可以在此处看到生成的代码而不是结果)。

这花了太长时间,我敢肯定还有一些空间可以挤出更多的字节,但是我再也看不到了:-p


2

Perl,99130字节

sub f{($_,$n)=@_;s/(-?\d+)?~(-?\d+)?|(-?\d+)/!(defined$3?$n!=$3:length$1&&$1>$n||length$2&&$n>$2)+0/ge;s/skip/&&!/g;s/,/||/g;eval}

在Ideone上尝试。

取消高尔夫:

sub f {
    my ($e, $n) = @_;

    $e =~ s/(-?\d+)?~(-?\d+)?|(-?\d+)/ (defined($3) ? $n == $3 : (!length($1) || $n >= $1) && (!length($2) || $n <= $2)) + 0 /ge;
    $e =~ s/skip/ && ! /g;
    $e =~ s/,/ || /g;

    return eval($e);
}

1
输入-2的〜-2失败。另外插入-时?在所有三个\ d *之前
Kjetil S.

@KjetilS。固定为负数和零。
丹尼斯·伊巴耶夫

好的代码(通常不需要完整的语法分析,更容易使用正则表达式)
Kjetil S.

1

JavaScript(ES6),221 292 287 309 274 277 278字节

(-5字节归功于Okx)

(j,v,m=1/0,Z=/(skip)([^,]+)/g)=>eval(j[M='replace'](/(-?\d*)~(-?\d*)/g,(e,a,b)=>(a[M]('-','#')||-m)+'<='+(T=v[M]('-','#'))+'&&'+T+'<='+(b[M]('-','#')||m))[M](Z,i=o=>o.match(Z)?i(o[M](Z,'&&!($2)')):o)[M](/,/g,'||')[M](/(^|[^=&#\d])(\d+)([^<\d]|$)/g,'$1$2=='+v+'$3')[M](/#/g,'-'))

哇。由于所有的极端情况,这并不容易,但是我想我做到了。我只是希望没有任何特殊情况可能破坏使用的正则表达式。我将尽可能多地打高尔夫球。

测试片段

D=(j,v,m=1/0,Z=/(skip)([^,]+)/g)=>eval(j[M='replace'](/(-?\d*)~(-?\d*)/g,(e,a,b)=>(a[M]('-','#')||-m)+'<='+(T=v[M]('-','#'))+'&&'+T+'<='+(b[M]('-','#')||m))[M](Z,i=o=>o.match(Z)?i(o[M](Z,'&&!($2)')):o)[M](/,/g,'||')[M](/(^|[^=&#\d])(\d+)([^<\d]|$)/g,'$1$2=='+v+'$3')[M](/#/g,'-'))
MPN Expression:<input type="text" value="1~9 skip (2~8 skip (3~7 skip (4~6 skip 5)))" id="MPN"></input>
<br>
Integer:<input type="number" id="INT" value=6></input>
<input type="button" value="Submit" onclick="T=r=>document.getElementById(r).value;console.log(D(T('MPN'),T('INT')))"></input>


@AriaAx现在应该可以工作了。
R. Kap

@ R.Kap你可以使用 1/0Infinity
Okx

@ R.Kap我尝试6使用6 skip 6,~我认为应该是的表达式来赋值,true但它会返回false
briantist

@briantist其实,我认为,应该恢复falseskip适用于一切遵循它(6,~在这种情况下),只要它是不是在括号内包裹。因此,我认为它应该返回true(6 skip 6),~,而不是6 skip 6,~用整数输入6
R. Kap's

@briantist换句话说,6 skip 6,~不应匹配任何内容,因为它表示set {6}与set 之间的差异{6,-Infinity...Infinity}
R. Kap's

0

Röda + bc,183个字节

f x{{["x=",x,"\n"];replace" ","",",","||","skip\\(","&&!","skip([0-9~]+)","&&!($1)","(?<!~|\\d)(\\d+)(?!~|\\d)","x==$1","(\\d*)~(\\d*)","x>=($1)&&x<=($2)","\\(\\)",x;["\n"]}|exec"bc"}

这类似于PowerShell的答案(或者我认为是这样,我不了解PowerShell)。在输入流中,它将数字作为参数,将代码作为值,如下所示:main { push("1~9") | f(5) }

我认为它有效,至少可以解决所有测试用例。以下脚本可用于验证这一点。

main {
    readLines("/tmp/tests.txt") | split(sep=";") | for code, num, ans do
        push(code, " -> ")
        print(code) | replace" ","",",","||","skip\\(","&&!","skip([0-9~]+)","&&!($1)","(?<!~|\\d)(\\d+)(?!~|\\d)","x==$1","(\\d*)~(\\d*)","x>=($1)&&x<=($2)","\\(\\)",num
        a := push(code) | f(num) | head()
        result := push("OK") if [ (a = "0" and ans = "False") or (a = "1" and ans = "True") ] else push("FAIL")
        print code, " ; ", num, " -> ", a, " ", ans, " (", result, ")\n"
    done
}

和测试:

10~20;14;True
10~20;20;True
10~20 skip 14~18;17;False
~ skip 6;8;True
16,17 skip 16;16;True
(16,17) skip 16;16;False
~10,5~;8;True
~10,5~;4;True
6 skip 6,~;6;True
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.