我解过的最难的 JavaScript 谜题
我一直都很喜欢代码谜题。我的同事跟我讲了一个谜题,办公室里没人能解出来。我被它吸引,不仅是因为它很有挑战性,还因为它是一个 JavaScript 谜题。
如果是 Project Euler 的问题(它们可能涉及数学),或者如果没有人找到 LeetCode 上某个“困难”问题的最优解(我见过一些曾经是研究论文的问题),我都能理解。
我花了两个星期才解开这个谜题。我几乎立刻就卡住了,然后这个问题就一直萦绕在我的脑海里,直到我突然想到答案。首先,我们来看看这个问题的简单版本,它帮助我破解了更难的版本。
除非你想被剧透,否则不要滚动太快。
Codewars:多行任务++:Hello World
-
f编写一个返回以下值的函数Hello, world!。 -
每行最多只能有 2 个字符,总行数必须少于 40 行。
不考虑线路限制。解决方案是:
function f () { return 'Hello, world!'; }
// or
const f = _ => 'Hello, world!';
我开始拆分代码并简化变量声明。我们可以去掉 `<string>` const,让函数存在于全局作用域中。我们还可以使用模板字符串将字符串拆分成多行。
出现错误。
f
=_
=> // Uncaught SyntaxError: Unexpected token '=>'
`H
el
l,
w
or
ld
!` // This string has newline characters in it!
我的下一个想法是在对象内部定义函数,然后从对象中检索该函数。
f=
{g
()
{
return 'Hello, world!'; // Too long!
}}
[`
g
`[
1]
]
// 'Beautified'
f = {
g() {
return 'Hello, world!';
}
}
[`
g
` [
1
]]
没有报错,我声明了一个函数,但现在我不知道如何在不使用关键字的情况下从内部return函数返回。感觉好像快要成功了,但其实还差一点。我仍然卡在如何定义不包含换行符的字符串上。
寻找灵感:JSFuck
JSFuck 是一种基于 JavaScript 原子结构的深奥且具有教育意义的编程风格。它仅使用六个不同的字符来编写和执行代码。
阅读这个项目的源代码真的让我大开眼界,让我了解了一些 JavaScript 的知识,这些知识只有在编写库或进行代码高尔夫之类的活动时才会用到。
一旦我弄明白如何从消息中移除换行符Hello, world!(用反斜杠转义\),其他一切都迎刃而解了。现在我可以用方括号括[]起对象来运行几乎任何代码。然而,其中一个要求是总行数必须控制在 40 行以内(这也是为什么不能使用 JSFuck 编码的原因之一)。
我的想法是通过对字符串方法调用Function.prototype.bind来创建一个新函数。我使用了String.prototype.trim,因为它生成的名称最短(而且还方便地去掉了所有尾随的换行符)。
f=
''
[
'\
t\
r\
i\
m'
][
'\ // Get a new function where `this` is "Hello, world!"
b\
i\
n\
d'
]` // Tagged template, see below
H\
e\
l\
l\
o\
,\
\
w\
o\
r\
l\
d\
!`
Hello, world!我还使用了带标签的模板作为参数传递给绑定函数。
标签允许你使用函数解析模板字面量。标签函数的第一个参数包含一个字符串值数组。
让我们把难度提升到一个更高的层次,回到这段旅程的起点!
Codewars:多行任务∞:Hello World
-
f编写一个返回以下值的函数Hello, world!。 -
每行最多只能有 1 个字符,总行数必须少于 145 行。
如果没有先解决每行两个字符的版本,我想我根本不可能回答这个版本的问题。
我采用的解决方案相同,我们使用bind`on`trim并将消息作为参数传递(这次没有模板标签)。要访问字符串对象,我们使用 `.`,[]+[]其值为 ` "".`
由于我们无法再从模板字符串中转义换行符,因此必须使用变通方法。属性名称(trim,bind)和消息(Hello, world!)必须使用连接变量构建。
我们对模板字符串使用解构赋值,并在第一个数组中使用空槽来“跳过”将换行符赋值给任何元素。就像这样:
[,a,,b,,c] = 'xHxix!' // avoid the "x"s
a + b + c // Evaluates to: "Hi!"
一旦方案满足了要求,我就没有再进行优化。为了更好地解释其运行机制(例如,模板字符串中只需要一个“l”),代码仍然比较冗长。
[ // With destructuring assignment, start declaring variables
,
t
,
,
r
,
,
i
,
,
m
,
,
b
,
,
i
,
,
n
,
,
d
,
,
H
,
,
e
,
,
l
,
,
l
,
,
o
,
,
c // Comma
,
,
s
,
,
w
,
,
o
,
,
r
,
,
l
,
,
d
,
,
x // Exclamation mark
]
=
`
t
r
i
m
b
i
n
d
H
e
l
l
o
,
w
o
r
l
d
!
`
f // Start declaring our function
=
( // This evaluates to "" or, the String object
[
]
+
[
]
)
[ // `trim`
t
+
r
+
i
+
m
]
[ // `bind`
b
+
i
+
n
+
d
]
( // Can use parentheses or template tag syntax to call `bind`
H
+
e
+
l
+
l
+
o
+
c
+
s
+
w
+
o
+
r
+
l
+
d
+
x
)
我绝对要暂时放下与特定编程语言相关的编程难题了——比起语法,我更喜欢逻辑!不过,我很高兴自己满足了这方面的渴望。
已有超过 150 人订阅了我的编程和个人成长简讯!
在推特上关注@healeycodes,了解科技相关内容。
文章来源:https://dev.to/healeycodes/hardest-javascript-puzzle-i-ve-ever-solved-bci