知乎高问:965月薪18k有必要跳槽大厂30k吗?
刚看到个贴子,说一哥们在北京,965月薪18k,不加班不裁员,就是两年没涨薪,现在拿到大厂30k的offer,纠结要不要跳。
我觉得这事吧,主要看你追求啥。如果图稳定、生活平衡,那现在这份工是真的香,四小时干活还能拿18k,北京能过得挺滋润的。但要是你想提升技术、拓展眼界,那跳槽去大厂确实是条捷径——不过得准备好被“加班文化”卷进去了。
算法题:Lisp 语法解析
说个场景先哈——昨天晚上十一点多,我在公司楼下啃汉堡,手机里有人问:“哥,面试要我手写个 Lisp 解析器,用 Java,咋整?”我一想,这不就括号地狱嘛…但别慌,套路很稳。
Lisp 的核心其实就三样:原子(数字、符号、字符串)、列表((a b (c d)))、引号('x 等于 (quote x))。目标是把源码变成一棵 AST,然后你愿意再做解释执行也行,不愿意就停在 AST 也能交差。
先分词:括号单独成词,字符串要成对儿,空白分隔。再递归下降:
遇到 '就把后面那个表达式包成(quote …)。遇到 (就一直读到匹配的),生成 List 节点。其他就是 Atom。 有个小坑:要报告未闭合括号、非法字符串,面试官最爱问的就是这些边界。
Java 代码
import java.util.*;
classLispParser{
// --- AST ---
interfaceNode{ }
staticfinalclassAtomimplementsNode{
final String lexeme;
Atom(String s){ this.lexeme = s; }
public String toString(){ return lexeme; }
}
staticfinalclassListNodeimplementsNode{
final List<Node> children = new ArrayList<>();
public String toString(){ return children.toString(); }
}
// --- Lexer ---
static List<String> tokenize(String src){
List<String> out = new ArrayList<>();
int n = src.length();
for(int i=0;i<n;){
char c = src.charAt(i);
if(Character.isWhitespace(c)){ i++; continue; }
if(c=='('||c==')'||c=='\''){ out.add(String.valueOf(c)); i++; continue; }
if(c=='"'){ // string
int j=i+1; StringBuilder sb=new StringBuilder();
while(j<n && src.charAt(j)!='"'){
if(src.charAt(j)=='\\' && j+1<n){ sb.append(src.charAt(j+1)); j+=2; }
else { sb.append(src.charAt(j++)); }
}
if(j>=n) thrownew RuntimeException("Unclosed string");
out.add("\""+sb.toString()+"\"");
i=j+1; continue;
}
// symbol/number
int j=i;
while(j<n){
char d=src.charAt(j);
if(Character.isWhitespace(d) || d=='('||d==')'||d=='\'') break;
j++;
}
out.add(src.substring(i,j));
i=j;
}
return out;
}
// --- Parser ---
staticfinalclassP{
final List<String> t; int i=0;
P(List<String> t){ this.t=t; }
booleanend(){ return i>=t.size(); }
String peek(){ return t.get(i); }
String pop(){ return t.get(i++); }
Node parseExpr(){
if(end()) thrownew RuntimeException("Unexpected end");
String tok = peek();
if("'".equals(tok)){ // quote
pop();
ListNode q = new ListNode();
q.children.add(new Atom("quote"));
q.children.add(parseExpr());
return q;
}
if("(".equals(tok)){
pop();
ListNode list = new ListNode();
while(!end() && !")".equals(peek())){
list.children.add(parseExpr());
}
if(end()) thrownew RuntimeException("Unclosed paren");
pop(); // )
return list;
}
if(")".equals(tok)) thrownew RuntimeException("Unexpected )");
returnnew Atom(pop());
}
}
// demo
publicstatic Node parse(String code){
List<String> tk = tokenize(code);
P p = new P(tk);
Node root = p.parseExpr();
if(!p.end()) thrownew RuntimeException("Trailing tokens from: "+p.peek());
return root;
}
publicstaticvoidmain(String[] args){
String code = "(define square (lambda (x) (* x x)))";
System.out.println(parse(code));
System.out.println(parse("'(1 2 \"hi\")"));
}
}
数字和符号现在都进了 Atom,要区分类型,面试时你可以在 Atom 里多存个 kind;字符串我保留了引号,真实业务会存原始值;错误信息一定要给到点,比如哪一位出错。行了我得去接个电话…总之这版能把 Lisp 的基本语法稳稳解析下来,够用也好讲。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html