对实习leader有好感了,怎么办?
刚来实习一个月,就把leader看成心动嘉宾了,这味儿太熟了。办公室恋爱八字还没一撇,你先把“她没男朋友”都脑补完了,HR看见都得扶额。
这种事最怕的不是加不加V,是你这边刚上头,人家那边拿你当正常实习生。群里聊工作挺自然,一旦你单独去加,还没啥正当理由,确实容易把气氛搞拧。评论区也有人说得挺实在:别把欣赏误会成有戏;还有人劝,先混熟再说,别一上来就莽。
我觉得现在真别冲。你才来一个月,连她对你有没有额外印象都不知道。先把活干明白,多接触,看看线下相处是什么感觉。真有来回,再找个工作由头加,也不尴尬。实习期最值钱的不是恋爱脑,是别让自己以后想起来脚趾抠地。
面试题:Lisp 语法解析
一看到 "(let x 2 (mult x (let x 3 y 4 (add x y))))" 这种题,很多人第一反应就是递归。递归没错,但真写起来,最容易死在两处:一处是作用域,一处是指针推进。尤其是 let,变量不是全局替换,它是带作用域的,后面的同名变量还能把前面的盖掉,这地方我第一眼就不太信“简单 split 一下”这种写法。
这题本质不是算四则运算,而是边扫描、边建作用域、边求值。 像这段:
(let x 2
(mult x
(let x 3 y 4
(add x y))))
外层 x=2,但内层又来了个 x=3,所以 add x y 用的是内层变量,结果是 7,再乘外层的 x=2,最后才是 14。这种题如果你把变量表只写成一个 Map<String,Integer>,基本很快就会算错。
我一般会这么处理:递归解析表达式,每进一层括号,就复制一份当前作用域。代价不算小,但代码干净,面试场景也够稳。
先看核心入口,靠一个下标往前扫:
classSolution{
privateint idx = 0;
publicintevaluate(String expression){
return parse(expression, new HashMap<>());
}
privateintparse(String s, Map<String, Integer> scope){
if (s.charAt(idx) != '(') {
if (s.charAt(idx) == '-' || Character.isDigit(s.charAt(idx))) {
return parseInt(s);
}
return scope.get(parseVar(s));
}
不是左括号,说明当前要么是数字,要么是变量,直接取值。真正麻烦的是左括号里面那几个关键字:add、mult、let。
add 和 mult 都不难,读两个表达式,算完跳过右括号:
idx++; // 跳过 '('
String op = parseToken(s);
idx++; // 跳过空格
if ("add".equals(op)) {
int a = parse(s, new HashMap<>(scope));
idx++;
int b = parse(s, new HashMap<>(scope));
idx++;
return a + b;
}
if ("mult".equals(op)) {
int a = parse(s, new HashMap<>(scope));
idx++;
int b = parse(s, new HashMap<>(scope));
idx++;
return a * b;
}
真正容易绕进去的是 let。它不是固定两个参数,而是一串“变量名、表达式、变量名、表达式……最后一个结果表达式”。所以这里不能写死,只能边读边判断:
Map<String, Integer> local = new HashMap<>(scope);
while (true) {
if (s.charAt(idx) == '(' || s.charAt(idx) == '-' || Character.isDigit(s.charAt(idx))) {
int ans = parse(s, local);
idx++;
return ans;
}
String name = parseVar(s);
if (s.charAt(idx) == ')') {
return local.get(name);
}
idx++; // 空格
int val = parse(s, local);
local.put(name, val);
if (s.charAt(idx) == ')') {
return val;
}
idx++;
}
}
再补两个小工具方法,题就能跑通:
privateintparseInt(String s){
int sign = 1, num = 0;
if (s.charAt(idx) == '-') { sign = -1; idx++; }
while (idx < s.length() && Character.isDigit(s.charAt(idx))) {
num = num * 10 + s.charAt(idx++) - '0';
}
return sign * num;
}
private String parseVar(String s){
int start = idx;
while (idx < s.length() && s.charAt(idx) != ' ' && s.charAt(idx) != ')') {
idx++;
}
return s.substring(start, idx);
}
private String parseToken(String s){
return parseVar(s);
}
}
这题代码不长,但很吃细节。尤其两个点别丢: 第一,let 的作用域要分层,别偷懒共用一张表。 第二,idx 每次跳空格、跳右括号,位置要非常克制,多一下少一下,结果都乱。
这种题写完,别急着交。拿嵌套 let 多压两组数据,比你盯着代码看十分钟都靠谱。