HolyJS 2019:SEMrush汇报(第1部分)



在5月24日至25日于圣彼得堡举行的HolyJS JavaScript开发人员例会上,我们公司的展位为所有人提供了新任务。 这次有3个! 依次给出任务,并且对于每个后续任务的解决,都依赖于徽章(JS Brave> JS Adept> JS Master),这是一个不停的好动机。 我们总共收集了约900个答案,并急于分享对最受欢迎和独特的解决方案的分析。

期望从对语言的基本“功能”的勇敢和理解以及对ECMAScript 2019的新功能的认识(实际上,后者是不必要的)中预期提出的测试。 重要的是,这些任务不适合面试,不切实际,并且仅出于娱乐目的而考虑。

任务1〜倒数表达


什么将返回表达式? 重新排列任何单个字符以

  1. 表达式返回2
  2. 表达式返回1

+(_ => [,,~1])().length 

另外 :可以通过排列获得0吗?

什么将返回表达式?


不久之后,该表达式将返回3 。 我们有一个匿名函数,该函数仅返回三个元素的数组,其中前两个为空。 函数调用为我们提供了该数组,我们从中获取了长度,一元运算符plus无法解决任何问题。

表达式返回2


快速修复似乎可以减少返回数组中元素的数量。 为此,只需抛出一个逗号:

 [,~1].length // 2 

不可能像这样抛出一个符号:需要将其重新排列到原始表达式中的另一个位置。 但是我们知道,数组的length属性考虑了数组文字中列出的空元素,只有一种情况除外:

如果在数组的末尾省略了一个元素,则该元素对数组的长度没有贡献。

也就是说,如果一个空元素位于数组的末尾,则将其忽略:

 [,10,] // [empty, 10] 

因此,更正后的表达式如下所示:

 +(_ => [,~1,])().length // 2 

还有摆脱该逗号的另一种选择吗? 还是开车。

表达式返回1


通过减小数组的大小不再可能获得一个,因为您将不得不至少进行两个排列。 必须寻找另一种选择。

函数本身暗示了该决定。 回想一下,JavaScript中的函数是一个具有自己的属性和方法的对象。 属性之一也是length ,它确定函数期望的参数数量。 在我们的例子中,函数只有一个参数( 下划线 )-您需要什么!

为了从函数而不是数组获取长度 ,您需要停止通过括号来调用它。 显而易见,这些括号之一是排列的竞争者。 我想到了两个选择:

 +((_ => [,,~1])).length // 1 +(_ => ([,,~1])).length // 1 

也许还有别的东西吗? 还是下一级。

表达式返回0


在附加任务中,排列数量不受限制。 但是我们可以尝试通过做最少的手势来实现它。

通过从函数对象中获得丰富的经验,您可以快速找到解决方案:

 +(() => [,_,~1]).length // 0 

这里有几个导数,但总的来说是我们将函数参数的数量减少到零。 为此,我们需要多达三个排列 :两个方括号和下划线字符,它们成为数组的元素。

好的,但是也许是时候停止在我们的推理中忽略加法(+)和按位NOT(〜)运算符了吗? 看来这个问题中的算术可以发挥作用。 为了不至于起步,以下是原始表达式:

 +(_ => [,,~1])().length 

首先,我们计算〜1x 的按位NOT将返回-(x + 1) 。 也就是说, 〜1 = -2 。 而且,表达式中还有一个加法运算符,它暗示了您需要在其他地方再找到2个,否则一切都会成功。

最近,我们记得数组常量中最后一个空元素的“无效”,即其大小,这意味着我们的推论在这里:

 [,,].length // 2 

这一切都非常成功地相加:我们从数组中获得元素〜1 ,将其长度减小为2,并将其添加为表达式的开头的第一个操作数:

 ~1+(_ => [,,])().length // 0 

因此,我们已经在两个排列上实现了目标!

但是,如果这不是唯一的选择怎么办? 一点鼓声...

 +(_ => [,,~1.])(),length // 0 

它还需要两个排列:单位后的点(这是可能的,因为数字类型只是number ),而长度前的逗号是。 似乎胡说八道,但“有时”有效。 为什么有时呢?

在这种情况下,通过逗号运算符的表达式分为两个表达式,结果将是第二个表达式的计算值。 但是第二个表达式只是长度 ! 事实是,这里我们在全局上下文中访问变量的值。 如果运行时是浏览器,则为window.length 。 而且window对象确实具有length属性 ,该属性返回页面上的帧数。 如果我们的文档为空,则length将返回0。是的,一个带有假设的选项...因此,让我们关注上一个。

这是发现的一些更有趣的选项(已经有不同数量的排列):

 (_ => [,,].length+~1)() // 0 +(~([,,].len_gth) >= 1) // 0 ~(_ => 1)()+[,,].length // 0 ~(_ => 1)().length,+[,] // 0 ~[,,]+(_ => 1()).length // 0 

没有任何评论。 有人能找到更有趣的东西吗?

动机


关于“重新安排一两场比赛以取得一个正方形”的老式任务。 在此类JavaScript变体中,逻辑和创造性思维发展的众所周知难题变成了晦涩难懂的事物。 这有用吗? 否定可能性更大。 我们触及了该语言的许多功能,甚至有些概念性的内容,以使解决方案的底层。 但是,真正的项目与功能和表达到类固醇的长度无关。 该任务在会议上提出来是一种令人上瘾的锻炼,用以记住JavaScript是什么样的。

评估组合


并未考虑所有可能的答案,但是为了不遗漏任何内容,我们转向真正的JavaScript ! 如果您自己尝试找到它们很有趣,那么上面有足够的技巧,那么最好不要进一步阅读。



因此,我们有一个23个字符的表达式,我们将在其中的字符串记录中进行置换。 总的来说,我们需要在表达式的原始记录中执行n *(n-1)= 506个排列,以便获得带有一个排列符号的所有变体(如问题1和2的条件所要求)。

我们定义一个组合函数,将输入表达式作为字符串和谓词,以测试通过执行此表达式获得的值的适用性。 该函数将强制执行所有可能的选项,并通过eval评估表达式,将结果保存在一个对象中: key-接收到的值,value-该值针对表达式的突变列表。 这样的事情发生了:

 const combine = (expr, cond) => { let res = {}; let indices = [...Array(expr.length).keys()]; indices.forEach(i => indices.forEach(j => { if (i !== j) { let perm = replace(expr, i, j); try { let val = eval(perm); if (cond(val)) { (res[val] = res[val] || []).push(perm); } } catch (e) { /* do nothing */ } } })); return res; } 

表达式的传递字符串中的replace函数返回一个新字符串,该字符串的字符从位置i重新排列到位置j 。 现在,我们不用担心,我们将:

 console.dir(combine('+(_ => [,,~1])().length', val => typeof val === 'number' && !isNaN(val))); 

结果,我们得到了一套解决方案:

 { "1": [ "+(_ => [,,~1]()).length", "+((_ => [,,~1])).length", "+(_ =>( [,,~1])).length", "+(_ => ([,,~1])).length" ], "2": [ "+(_ => [,~1,])().length" ] "3": [/* ... */] "-4": [/* ... */] } 

3和-4的解决方案对我们不感兴趣,因为这两个是我们找到的唯一解决方案,对于单位,有一个有趣的新情况, [,,〜1]() 。 为什么不是TypeError:bla-bla不是一个函数 ? 一切都很简单:该表达式对于语法解析器来说没有错误,并且在运行时它根本不会执行,因为没有调用该函数。

如您所见,在一个排列中不可能解决零的问题。 我们可以尝试两次吗? 通过这种详尽的搜索来解决问题,在这种情况下,我们将具有字符串长度的复杂度O(n ^ 4) ,并进行了多次“评估”和惩罚,但出于好奇心盛行。 独立完善给定的合并函数或编写考虑特定表达式特征的更好枚举并不难。

 console.dir(combine2('+(_ => [,,~1])().length', val => val === 0)); 

最后,零的解决方案集将是:

 { "0": [ "+(_ => [,~.1])(),length", "+(_ => [,~1.])(),length", "~1+(_ => [,,])().length" ] } 

奇怪的是,在推理过程中我们将单元后的点重新排列,但是忘记了单元前的点也是可能的:记录0.1并省略零。

如果对每个字符执行所有可能的排列,您会发现对于3到-4范围内的值有很多答案:

 { "3": 198, "2": 35, "1": 150, "0": 3, "-1": 129, "-2": 118, "-3": 15, "-4": 64 } 

因此,在两个排列处的倒数表达式解决方案路径可能比从3到0的建议路径更长。

这是在HolyJS 2019中对我们的任务进行分析的第一部分,不久将出现第二部分,我们将在其中考虑第二和第三项测试的解决方案。 我们会保持联系!

Source: https://habr.com/ru/post/zh-CN452114/


All Articles