仅仅十五分钟就结束面试便接到了offer,这种运气放在当下的求职市场当中,就如同下楼去买一瓶水的时候顺便捡到了年终奖是差不多的情况。更为离谱的是,HR专门打来了电话,仔细认真地读了一大段关于面试的评价。你不用多说,这样的待遇在现在确实是相当稀缺的了,好多人投递一百份简历,连一个“已读”的反馈都无法求得。
评论区着实挺真实,有人讲,这表明岗位乃是在等待合适之人,而你正好契合那个时机;也有人带着酸味说,别得意得太早,招聘速度这般快,说不定是该岗位急需人补位;这些话都并无差错,可我审视此事,其关键核心应为公司起码看似如正常人那般,能够迅速给予反馈,能够认真地展开沟通,这说明其流程并未完全糟糕透顶,至少没有将候选人全然当作不存在的空气。
寻工作时最怕的并非是遭到拒绝,而是你煞费苦心精心筹备了许久,对面却仿若陷入死机状态毫无动静。当下若能遇见那种面试完毕即刻给出结果,并且还乐意好好交流沟通的人力资源专员,着实算是机缘幸运之事。
今日算法题: Lisp 语法解析
,一旦括号数量增多,递归操作运用起来,好多人的第一反应便是“这道题目难道不就是栈能解决的么”。可是真到动手去写的时候,就特别容易出现失误。对于Lisp语法解析这道题而言,难点并非在于“计算”,难点在于你要怎样将字符串一段一段地妥善处理干净:空格该如何跳过,变量作用域要怎样进行压入操作,当读到add、mult、let读到一半的时候,要怎么知晓应该结束处理了。在这个地方,我第一眼看到的时候,就不太相信那些直接使用一把split(' ')的写法,因为括号里面的表达式是能够继续嵌套的,直接进行切割的话,基本上都会失败。在整体的写法方面,老老实实依照游标朝着前面扫动才是最为稳妥的,从写法的气质角度来看,它更似于在线上对协议串展开处理,而非去做一道如同玩具般的题目。
这题核心就三件事:

先读取token,若读取到则判断,读取出的内容要么能够读出一个数字,要么能够读出一个变量名,要么会撞上(从而开始对其中子表达式予以解析,接下来再去梳理执行作用域,就像let x 2 y 3 (add x y) 这种情况,前面所执行的赋值操作仅仅只在当前表达式范围之内生效,一旦脱出此范围就需要进行还原,最后再去计算其值,add操作也就是将两个参数进行相加,mult操作就是进行相乘运算,let操作则是前面一系列的赋值,而最后一个才是真正用于返回的数值。
我一般会这么写,游标 i 全程共享,不回头,不切片太多:
functionevaluate(expression) {
let i = 0;
const isDigit = (ch) => ch >= '0' && ch <= '9';
functionskipSpace {
while (expression[i] === ' ') i++;
}
functionparseToken {
let start = i;
if (expression[i] === '-') i++;
while (i < expression.length && expression[i] !== ' ' && expression[i] !== ')') i++;
return expression.slice(start, i);
}
functionparseExpr(scope) {
if (expression[i] !== '(') {
const token = parseToken;
if (token[0] === '-' || isDigit(token[0])) returnNumber(token);
return scope.get(token);
}
i++; // 跳过 (
const op = parseToken;
skipSpace;
if (op === 'add') {
const a = parseExpr(newMap(scope));
skipSpace;
const b = parseExpr(newMap(scope));
i++; // 跳过 )
return a + b;
}
if (op === 'mult') {
const a = parseExpr(newMap(scope));
skipSpace;
const b = parseExpr(newMap(scope));
i++;
return a * b;
}
const local = newMap(scope);
while (true) {
skipSpace;
if (expression[i] === '(' || expression[i] === '-' || isDigit(expression[i])) {
const val = parseExpr(local);
i++;
return val;
}
const name = parseToken;
skipSpace;
if (expression[i] === ')') {
const val = local.get(name);
i++;
return val;
}
const val = parseExpr(local);
local.set(name, val);
skipSpace;
}
}
return parseExpr(newMap);
}
拿这个例子过一下:
console.log(evaluate("(let x 2 (mult x (let x 3 y 4 (add x y))) )")); // 14
外面一层设定 x 等于 2 ,而里面一层又定义了 x 等于 3 、 y 等于 4 ,因此 (add x y) 采用的是里面一层的值,得出结果为 7 ,最终外面一层变为 2 乘以 7 等于 14。这便是作用域栈的那种情况。不要以为题目表面简短 ,实际上考查的很类似解释器的简化版本。
该项题目时间复杂度基本上就是O(n),每一个字符大概扫一遍,真正容易出错的并非复杂度,而是细节,负数不要遗漏,let最后一个表达式并非赋值,而是返回值,变量覆盖时,仅仅影响当前层,还有就是不要用split强行拆分,嵌套一旦深起来就会开始出现括号对不上的情况。

CopyrightC 2009-2025 All Rights Reserved 版权所有 芜湖人才网 本站内容仅供参考,不承担因使用信息、外部链接或服务中断导致的任何直接或间接责任,风险自担。如有侵权,请联系删除,联系邮箱:ysznh@foxmail.com 鄂ICP备2025097818号-15
地址: EMAIL:qlwl@foxmail.com
Powered by PHPYun.