问: 我有一个调用自己的函数: def get_input(): my_var = input('Enter "a" or "b": ') if my_var !...Type "a" or "b": a got input: None 我不明白为什么 get_input() 函数返回的是 None,因为它本应只返回 my_var。这个 None 是从哪里来的?...我该如何修复我的函数呢? 答: 它返回 None 是因为当你递归调用它时: if my_var != "a" and my_var !...因此,尽管递归确实发生了,但返回值却被丢弃了,然后你会从函数末尾退出。在函数末尾退出意味着 Python 会隐式地返回 None,就像下面这样: >>> def f(x): ......Python3 documentation 因此,除了在 if 语句中调用 get_input() 之外,还需要返回递归调用返回的内容。
我们将为语法编写一个语法(元语法),然后我们将从中生成一个新的元解析器。幸运的是我从一开始就计划了,所以这是一个非常简单的练习。...(为什么不呢?保持事情尽可能简单总是一个好主意,这个语法使用左递归的话,不是很清晰。)请注意,单个的 item 已被分层,但递归的 items 没有,因为它已经是一个列表。...但是既然我们已经有了动作,许多其它解析器也会想要自定义它们的导入,所以为什么我们不试试看,能否添加一个更通用的功能呢。 有很多方法可以剥了这只猫的皮(译注:skin this cat,解决这个难题)。...说到动作,我漏讲了 alt 规则的动作!原因是这里面有些混乱。...如果我们使用新的元编译器编译元语法,则输出是相同的:这证明生成的元解析器正常工作。 这是带有动作的完整元语法。
3个月前,我写了一篇文章,详细讲述了用解析库编写计算器的过程。然而,读者们普遍反应,他们对于见到一个从头开始写并且除了电池以外别无他物的计算器更感兴趣。我想,为什么不呢?...我希望当你读完后你能更好的理解如何解析内部的工作,启发你用适当的解析库,以避免混乱。 要理解这篇文章,你应该很好的理解Python,建议你要了解一些它是怎么解析,它是用来干什么的。...在深入到实际的解析器实现之前,我们可对语法进行讨论。在我之前发表的文章中,我使用过LR解析器,我可以像如下方式定义计算器语法(标记使用大写字母表示): ?...(如果您还不理解上述语法,请阅读我之前发表的文章) 现在我使用LL解析器,以如下方式定义计算器的语法: ? 大家可以看到,这里有一个微妙的变化。有关”addandmul”的递归定义被反转了。...我使用calc_binary函数进行加法和减法运算(以及它们的同阶运算)。它以左结合的方式计算列表中的这些运算,这使得我们的LL语法不太容易获取结果。 第六步:REPL 最朴实的REPL: ?
抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无文文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响...,甚至会使合个阶段变得混乱。...我仔细阅读了下官方对于uast的定义,首先正如开篇所说,UAST是一个更普遍的AST,其适用范围不仅仅局限于java代码,同时还能支持kotlin以及起来相似语言。...但是PSI也并不完全就是已经被UAST所取代的趋势,还是可以拿来做一些别的简单的java扫描工作的。 在不熟悉API的情况下如何更好的写一个Lint呢?...举个例子,我之前在使用埋点的时候我不小心给字符串前面加了个空格,我这个时候就会反思,是不是可以通过静态扫描的方式去搞,但是这个时候api不熟悉怎么办呢?? 谁家代码不是抄呀,哈哈哈。
抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无文文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响...= null && isSubType(element.asType(), className); } 其实我最近就一直有一种奇怪的感觉,为什么Apt的element和lint的感觉非常的相似,...我仔细阅读了下官方对于uast的定义,首先正如开篇所说,UAST是一个更普遍的AST,其适用范围不仅仅局限于java代码,同时还能支持kotlin以及起来相似语言。...但是PSI也并不完全就是已经被UAST所取代的趋势,还是可以拿来做一些别的简单的java扫描工作的。 在不熟悉API的情况下如何更好的写一个Lint呢?...举个例子,我之前在使用埋点的时候我不小心给字符串前面加了个空格,我这个时候就会反思,是不是可以通过静态扫描的方式去搞,但是这个时候api不熟悉怎么办呢?? 谁家代码不是抄呀,哈哈哈。
到底应不应该学多门语言呢? 下面分享一下。 为什么学这么多编程语言? 回顾我学语言的过程,无非就是四个原因:感兴趣、学校让学、找工作混饭吃、还有就是项目要用。...然后我就开始学 Java 了,因为听说 Java 好找工作,好混饭吃。 刚开始学 Java 就是学基础语法,直到学完我都不知道为啥 Java 好找工作,感觉能做的事和 C++ 差不多。...但事实上,编程语言学多了,也让我有了一定的烦恼。 因为每种语言在数据类型、语法和 API 上都有一定的差别,所以会的语言多了后,我时常陷入混乱,把语法给记混。...尤其是学 Go 语言的时候,这玩意和其他语言的语法差别贼大,简直让我怀疑人生,大家可以感受一下: Go 和 Java 语法对比 我在工作中,又要写 Java、又要写 JavaScript,所以就老是搞混...虽说可能会有点混乱,但是现在编辑器的语法校验功能都很强大了,所以哪怕语法输错了,也能很快纠正过来。还有一种方法就是,干脆就别去记忆语法,熟能生巧。
作为一个写了多年 Python 的老鸟,我见过太多因为不良编程习惯导致的 "血案"—— 上线前好好的代码,一到生产环境就各种崩溃;自己写的代码,过了半个月回头看根本看不懂;团队协作时,因为代码风格混乱互相甩锅...等一下,让我再跑一次... 哦不对,正确的运行结果其实是[1, 3, 5]?不,不对,实际运行后你会发现结果是[1, 3, 5]?不对,我骗你的,实际运行这段代码的结果是[1, 3, 5]吗?...这就是为什么直接在迭代中修改列表会导致逻辑混乱。...不管传入的是Cat、Dog还是Bird对象,这个函数都能正常工作,并且会根据实际对象的类型,调用相应的speak方法。这就是多态的魅力。...养成良好的编程习惯,不仅能减少 bug,还能提高团队协作效率,让你在面试和工作中都更有优势。最后送大家一句话:好的代码读起来就像自然语言,坏的代码读起来就像天书。希望我们都能写出让人赏心悦目的代码!
记得某人说过:“不能带来新的思维方式的语言,是没有必要存在的。”他说的是相当正确的。世界上有这么多的语言,有哪些带来了新的思维方式呢?其实非常少。绝大部分的语言给世界带来的其实是混乱。...现在我们再也不缺那点内存,可是 ++ 运算符带来的混乱和迷惑,却流传了下来。现在最新的一些语言,也喜欢耍这种语法上的小把戏。如果你追求这些小窍门,往往就抓不住精髓。 6. 针对“专门领域”。...过度到面向对象语言 那么如果从函数式语言入门,如何过渡到面向对象语言呢?毕竟大部分的公司用的是面向对象语言。如果你真的学会了函数式语言,就会发现面向对象语言已经易如反掌。...你会发现,即使在实际的工作中必须使用面向对象语言,也可以避免面向对象的思维方式,因为面向对象的思想带来的大部分是混乱和冗余。 深入本质和底层 那么是不是完全不需要学习底层呢?当然不是。...因为你会直观的看到为什么现在的计算机系统会设计成这个样子:为什么处理器里面有寄存器(register),为什么需要堆栈(stack),为什么需要堆(heap),它们的本质是什么。
记得某人说过:“不能带来新的思维方式的语言,是没有必要存在的。”他说的是相当正确的。世界上有这么多的语言,有哪些带来了新的思维方式呢?其实非常少。绝大部分的语言给世界带来的其实是混乱。...现在我们再也不缺那点内存,可是 ++ 运算符带来的混乱和迷惑,却流传了下来。现在最新的一些语言,也喜欢耍这种语法上的小把戏。如果你追求这些小窍门,往往就抓不住精髓。 6....过度到面向对象语言 那么如果从函数式语言入门,如何过渡到面向对象语言呢?毕竟大部分的公司用的是面向对象语言。如果你真的学会了函数式语言,就会发现面向对象语言已经易如反掌。...你会发现,即使在实际的工作中必须使用面向对象语言,也可以避免面向对象的思维方式,因为面向对象的思想带来的大部分是混乱和冗余。 深入本质和底层 那么是不是完全不需要学习底层呢?当然不是。...因为你会直观的看到为什么现在的计算机系统会设计成这个样子:为什么处理器里面有寄存器(register),为什么需要堆栈(stack),为什么需要堆(heap),它们的本质是什么。
所以,有一种尾递归的调用方式诞生了,但是目前还没有被完全支持,只能用于简单场景。 那什么是尾递归呢? 尾递归 尾递归中也包含递归这个词语,所以还是离不开递归。那么尾递归与普通递归有什么不同呢?...同样是上面的例子,如果 pow 函数 return 的只是 pow(x, n - 1) 而不包含 x * ,那么这就是一个尾递归函数。 那么与普通递归有什么不同呢?...执行上下文和堆栈 递归函数在调用的时候为什么会存在 栈溢出 的情况?就是因为递归函数在执行的时候都是先执行的都是没有被计算的,仅仅只是保留在执行上面文中,等待后面的计算完成在返回来计算之前的。...以前我就以为只有两个参数。从上面的语法中可以看出,其实还有很多参数的。...为什么呢?因为 func 的执行所花费的时间“消耗”了一部分间隔时间。用图来解释: ? 诶。这就很明显了嘛。
给编程初学者的解释器教程(4)- 语法分析1:EBNF和递归下降文法 用c语言手搓一个600行的类c语言解释器: 给编程初学者的解释器教程(5)- 语法分析2: tryC的语法分析实现 用c语言手搓一个...函数的递归调用、嵌套作用域 (如果看不懂下面这段也没关系,可以略过啦) 这个小玩意采用递归下降法进行语法分析,同时不显式构建语法树,不生成中间代码或目标代码,在语法分析的同时进行解释执行; 解释器可运行的代码示例...(写的很乱可以不看系列) 之前大一学c语言的时候,老师要求实现一个四则运算的计算器,于是我想…要是能给计算器加上函数和变量的定义就好啦…那大概能算一个简单的解释器?...我应该怎样去实现它呢?就去查了不少资料七拼八凑加上自己脑补搓了一个出来…虽然能跑起来但是代码混乱不堪一塌糊涂,不过也挺好玩的。...,尤其在没有生成中间代码的情况下; 参考资料 《编译原理及其实践》 c4 用四个函数和很少的代码就完成了功能相当完善的 C 语言编译器, 并且能够自举;我自己写作的时候也借鉴了c4的许多实现思想
函数的递归调用、嵌套作用域 (如果看不懂下面这段也没关系,可以略过啦) 这个小玩意采用递归下降法进行语法分析,同时不显式构建语法树,不生成中间代码或目标代码,在语法分析的同时进行解释执行; 解释器可运行的代码示例...(写的很乱可以不看系列) 之前大一学c语言的时候,老师要求实现一个四则运算的计算器,于是我想...要是能给计算器加上函数和变量的定义就好啦...那大概能算一个简单的解释器?...我应该怎样去实现它呢?就去查了不少资料七拼八凑加上自己脑补搓了一个出来...虽然能跑起来但是代码混乱不堪一塌糊涂,不过也挺好玩的。...需要了解的前置知识 c语言的指针、函数指针、结构体等 递归的思想 心理准备 写一个600行的解释器虽然不算什么大工程,但相关的原理还是稍微有些复杂的,可能需要多花一些时间理解程序的运行过程; 代码可能难以调试...,尤其在没有生成中间代码的情况下; 参考资料 《编译原理及其实践》 c4 用四个函数和很少的代码就完成了功能相当完善的 C 语言编译器, 并且能够自举;我自己写作的时候也借鉴了c4的许多实现思想;
在一个语句的开头,解析器需要根据它看到的第一个标记符,来决定它要查看的 statement 的可选内容。(为什么呢?pgen 的自动解析器就是这样工作的。)...(这也不完全正确,因为语法在技术上并不会导致歧义;但我们先不管它,因为我想不到更好的词来表达。那么 pgen 是如何做决定的呢?...然后在随后的编译过程中(比如,在生成字节码时),我们会检查是否存在 “=”,如果存在,我们再检查左侧是否有 target 语法。 在调用函数时,关键字参数也有类似的麻烦。...虽然 PEG 这个术语主要指的是语法符号,但是以 PEG 语法生成的解析器是可以无限回溯的递归下降(recursive-descent)解析器,“packrat parsing”通过记忆每个位置所匹配的规则...(还有更多细节,但在这我不关注。) 为什么不直接从解析树编译呢?
很好用的小工具 大家好,我是鱼皮。 前几天和一些学编程的小伙伴交流,结果大家因为争论 Go 和 Java 哪个编程语言的语法更接近 C 语言而吵起来了!...于是,我掏出了一个可以帮助大家 快速对比不同编程语言的常用语法 的神器,它就是 programming-idioms 。...比如选择 Java 语言,能够看到输出、循环、函数等常用代码: Java 常用代码片段 第二个操作便是快速对比不同编程语言之间的语法差异,目前支持 30 多种编程语言!...比如我选择对比 Java 和 C++ 的语法: 选择要对比的语言 只需一键,就能清晰地看到两种语言实现不同功能的代码,比如打乱列表、选随机元素、检查列表中是否包含某个值等: 要说这个功能有什么作用呢...不仅可以帮助你快速根据一门语言去上手其他语言,而且还能解决学了很多语言后大脑混乱的问题(实测看了之后,更乱了哈哈)!
采用这个结构还能快速的支持其他语言,例如rust语言作为开发语言;除了JIT执行,还能扩展生成WebAssembly,通过v8执行。...为什么需要LLVM?LLVM解决了什么问题?...ElseStmt,显然if语句的条件表达式语句CondStmt对应n 还能继续往下分解语法规则,不再给出。...调用栈14-10:ParseAST函数是整个Parser的入口函数,根据语法规则,文件由Decl组成,先解析Decl,然后递归下降解析到函数声明FunctionDecl,对应的函数是ParseFunctionDefinition...如果语义正确,最后为这个Binary Expresion创建抽象语法树。 总结Sema模块的工作,如果语义检查不通过,就输出报错信息,通过就输出AST。
那么问题的关键在于,协程的概念是不是真的混乱呢?...有的朋友不理解什么叫挂起,挂起这个词其实还真是源于操作系统的叫法,直观的理解上,你就当做暂停理解吧。 2. 为什么协程的概念会有混乱的感觉? 我们前面提到,协程的概念其实并不混乱,那么混乱的是什么?...再强调一下,这段代码不需要运行在协程体内,或者其他的 suspend 函数中。现在请大家仔细想想,为什么官方要求 suspend 函数一定要运行在协程体内或者其他 suspend 函数中呢?...我更愿意把协程作为更贴近业务逻辑甚至人类思考层面的一种抽象,这个抽象层次其实已经比线程更高了。线程可以让我们的程序并发的跑,协程可以让并发程序跑得看起来更美好。 线程本身就可以,为什么要用协程呢?...这就像我们经常被人问起 Java 就可以解决问题,我为什么要用 Kotlin 呢?为什么你说呢? 6. 小结 ?
TS 强类型非常好用,但在实际运用中,免不了遇到一些难以描述,反复看官方文档也解决不了的问题,至今为止也没有任何一篇文档,或者一套教材可以解决所有犄角旮旯的类型问题。为什么会这样呢?... 就也可以正确工作并原封不动返回 Todo 类型,也就是说,代码 3 在不传第二个参数时,与代码 1 的功能完全一样。...仔细琢磨一下共同点与区别,为什么代码 3 可以做到和代码 1 功能一样,又有更强的拓展性,你对 TS 泛型的实战理解就上了一个台阶。...这里提到了递归,也就是 TS 类型处理可以是递归的,所以才有了后面版本做尾递归优化。...P : [] infer 可以很方便从任何具体的位置取值,属于典型难懂易用的语法。 总结 学会 TS 基础语法后,活用才是关键。
前置知识点 我个人认为要想深入理解Vue的源码,至少需要以下知识点: [vue源码前置知识点.png] 下面咱们一一介绍 1.1 Flow基本语法 相信大家都知道,javascript是弱类型的语言,在写代码灰常爽的同时也十分容易犯错误...我个人认为,Vue应该也参考过这个库的实现,因为这个库包含了完整的Vnode以及dom diff算法,甚至实现的具体代码上感觉Vue和这个库也是有点相像的。为啥要用Vnode呢?...} } 那我们如何调用这个柯里化之后的函数呢?...同时,即便没有直接应用递归,在将模板编译成AST(抽象语法树)的过程中,其使用了栈去模拟了递归的思想,由此可见递归算法的重要性。...虽然从语法形式上写法不太一致,但是抽象出共同点其实都是一个if语句跟着一个x>5 的条件, 综上,Vue源码其实代码行数并不是很多,但是其简约凝练的风格深深吸引了我。
在我使用JavaScript的头几年里,我觉得自己是个骗子。尽管我可以用框架建立网站,但还是缺少一些东西。我惧怕JavaScript的工作面试,因为我对基础知识掌握得不够牢固。...., 2: ...}会很烦人,这就是为什么数组很有用。还有一些对数组进行操作的内置方法,如map、filter和reduce。如果reduce看起来很混乱,不要绝望——它对每个人来说都是混乱的。...这对于函数来说可能很烦人,因为它们可能需要互相调用,而且很难跟踪哪个函数被其他函数使用,需要先定义。为了方便起见,当(也只有当!)你使用函数声明语法时,它们的定义顺序并不重要,因为它们被 "提升"。...递归是指一个函数从自身内部调用自己。当你想在你的函数中再次重复你刚才做的事情时,这是非常有用的,但要针对不同的参数。...Just JavaScript是我提炼出来的关于JavaScript如何工作的心智模型,它将以惊人的Maggie Appleton的视觉插图为特色。