快速入⻔不求甚解的线段树
2021 年 6 月 8 日
什么是快乐星球线段树?如何以 log n 的复杂度做范围查询和修改?
随便写了写,也许有用。
2021 年 6 月 8 日
什么是快乐星球线段树?如何以 log n 的复杂度做范围查询和修改?
随便写了写,也许有用。
2021 年 5 月 20 日
印象中不少客户对网购的物流服务的好评会用上“包装严实”这个词。但我们真的需要“包装严实”吗?我们需要的只是物品完好无损吧。严实的包装拆起来可能贼费劲儿,我真巴不得不包装——若是仍能完好无损地送到。
后记:认真想了想,某些需要修改收件人姓名的特殊物品好像确实需要“包装严实”……
2021 年 5 月 11 日
洗澡的时候隐约觉得找人包养与去工厂打工本质上好像也没什么不同,毕竟说白了都是为别人提供价值。只不过找人包养是为个别人提供某个价值,市场较小,不太稳定。而去工厂打工是为很多人提供某个价值,市场较大,因此也比较稳定。
说到实现自己的价值,也许找人包养比起修福报更容易实现自己的价值,还真说不准。
2021 年 5 月 14 日
我想谈一谈我对 call-by-value、call-by-name、call-by-need、call-by-reference 的理解。这只是我的理解,应该与 Wikipedia 有出入——但我仍然坚持我的理解。
Call-by-value 与 call-by-name 放在一起比较时,call-by-value 表示函数调用时,实参要在调用函数前求到一个值。
Call-by-value 与 call-by-reference 放在一起比较时,call-by-value 表示函数调用时,实参的地址中的内容,而不是地址本身,被传入函数。
所以,call-by-value 这个词在不同的语境下的含义(居然)是截然不同的,这就是问题的根源。
在(一些)函数式语言的世界,如 Haskell,变量是不可被再次赋值的。所以假使有 let x = True
,那么 x
和 True
可以划上等号:凡是见到 x
的地方,都可以替换成 True
。因为 x
不可变,我们也不需要关心 x
究竟是存储在内存中,还是存储在火星上,或者说我们有魔法,x
不占用任何存储空间——这根本不重要,x
就是个常量,我不会去找到 x
再它把它的内容换掉。
现在我们解释值这个概念。程序语言理论中,一门简单的语言可以只有表达式(expressions),或称之为项(terms),并且这些项最终都可以计算成值(values)。换句话说,值是不可再计算的项,它通常可以是项的子集。
例如,可以认为 1
是值但 2 + 3
不是,因为 2 + 3
还可以计算到 5
。或者说项 2 + 3
的值是 5
,而项 1
的值就是 1
本身。
有了值的定义,便可以解释 call-by-value、call-by-name、call-by-need。
对于函数定义 f(x) = x + x
,项 t = 2 * 3
和函数调用 f(t)
t
的值求出来,即 6
,再将其传给形参 x
,进行函数调用。t
直接传给形参,进行函数调用。也就是说,函数体内的(自由出现)的 x
都被替换为 t
,即函数体变为 t + t
。此时才会去求 t
的值,它还被求了 2 次。t
只求一次,可以用 memoization 实现。小 tip:call-by-name 不一定就比 call-by-value 慢——例如,如果 name 在函数体内没被用到,则它根本不会被计算。
到这里,一切安好。
然而,在函数式之外的世界,写程序几乎就是创建一些存储单元,再不断改变这些存储单元的内容——从图灵机开始,我们便知道纸带有左移右移操作和读写操作——这便是“万恶的源泉”。
为了方便地访问预先创建的存储单元,我们给这些存储单元加上标签,并起名为变量。例如,int a = 0;
这条语句创建了一块存储单元,将其中的内容设置为 0
,并且可以用标签(变量) a
方便地找到。
那么就产生了一个严肃的问题。当我提到 a
的时候,我到底指的是那一块存储空间呢,还是那一块存储空间里面的内容 0
呢?a = a + 100;
这条赋值语句中,等号左右的 a
都是些什么货色呢?大多数命令式语言,为了方便书写程序,都会自动将 =
左边的 a
解释为存储空间(的别名),而将 =
右边的 a
解释为存储空间中的内容。所以 a = a + 100
执行后,a
存储单元的值就从 0
变成了 100
。
因此,存储单元——引用,reference;存储单元的内容——值,value。
这就有了 call-by-value 和 call-by-reference 的区别。还是对于函数定义 f(x) = x + x
,项 t = 2 * 3
和函数调用 f(t)
t
的存储空间的内容传给实参。t
的存储空间本身的地址、位置传给实参。所以这里的 call-by-value 和 call-by-reference,与上一小节的 call-by-value 和 call-by-name,根本就是两个不相交的维度的概念,但却都使用了 call-by-value 这个词。
所以说,call-by-value 在不同语境下会有不同的含义,既可以指代将 term 计算成 value 再做函数调用,也可以指代将存储地址的内容(而非地址本身)传给函数的参数。
大多数语言都是将 term 计算成 value 再做函数调用(也即 strict),如 C 系、Java 系(好吧,jvm 系)。也有 call-by-name 和 call-by-need 的(统称为 non-strict),如 Haskell。
C 系语言一般是传存储单元的值,array 例外。如果想传存储单元的地址,需要使用 &
符号。Java 系语言一般是传存储单元的地址,并且,(暂时还)没办法传存储单元的值。
思考题:设有函数
1 | fn f(x) = |
如果使用 call-by-name,f(2+3)
会得到什么样的函数体?x := x + 100
会变成 2 + 3 := 2 + 3 + 100
吗?
后来竟然觉得 call-by-reference 可能说的是这种:变量 obj 有自己的地址,地址中存着一个值,但这个值永远是个固定大小的 pointer,指向(heap 里的)某个真实的存储空间——所以它仍然可以看作是地址。函数调用的时候就传那个指向(heap 里的)某个真实的存储空间的 pointer。遂去查了查,这个居然叫 call-by-sharing?哎呀哎呀,行吧,感觉这一领域术语太多太乱,会用就行,不需特别理解。
在编程语言这一点上,其实本人知道的也比较有限,只是比普通程序员多懂一点。
以前读书的时候感觉周围的 PL(即 programming languages)研究者们关心的都是语言的“前中端问题”,例如类型系统和语义等;并没有太多与虚拟机、垃圾回收、IR 优化相关的话题。所以我这边推荐的入门书籍也还是以类型系统和语义为主的。
对这些入门书籍的学习不仅仅是可以很好地了解编程语言的类型系统与语义,以至为 PL 专业的硕士、博士研究生铺平道路。更重要的是可以了解 turing-machine 之外的另一种计算模型,即 lambda calculus;可以了解到大家熟悉的 imperative、state-based、loop-based 之外的另一种编程思想,即 declarative、function-based、recursion-based 的编程思想——这种新的思想不一定能带来代码运行速度上的提升,但却几乎一定能带来抽象能力、问题拆解能力的提升。
废话说得比较多了,下面是一些本人有所了解的书籍,有些可能比较旧了。而且书籍这种东西,对其评价或许也是带有很强的主观色彩的。
《Types and Programming Languages》,即耳熟能详的 TAPL,作者是 Pierce, Benjamin C。这是一本 2002 年出版的书,挺老了,但仍然是一本很棒的 PL 入门书。通过阅读(和动手),我们会学到类型系统和语义的相关知识,以及 lambda calculus 的入门知识。可以从中系统地了解多态(即 polymorphism,如 Java 泛型,C++ 模板)的几个维度:类型依赖类型(如 class List<X>
),项(类型的成员)依赖类型(如 <T> boolean contains(Collection<T> c)
),以及类型依赖项(常用来写证明,无法用 Java 举例);而最后一个最为常见的且抽象层级比较低的,项依赖项,就是大家最为熟悉的函数或者说方法。书中还会详尽地介绍子类型系统、交类型、并类型,以及子类型中的型变。这些知识对于日常编程以及熟练掌握一门程序语言自然是大有裨益的。此外,贯穿全书的是语言的类型检查、语义的形式化写法,以及两个重要结论及其证明:1 通过类型检查的代码要么是值,要么可以继续计算到值(progress),2 通过类型检查的代码,计算后的结果仍然可通过类型检查(preservation)。考察一门语言时,这两点也是要重点审视的。另外,就中国内地来说,北京大学有开课,可以参考,课件或许更适合中国人学习?(有一个比较精简的,Luca Cardelli 的《Type Systems》,40 几页,复习的时候用挺好。)
《Practical Foundations for Programming Languages》,即 PFPL,作者是 Robert Harper(官方免费版戳此)。广度感觉特别广(特别是相对于上一本来说),覆盖了方方面面。唉,但一直没时间看,或者说每次看到 total functions 那里都中断了……我把大的主题(每一个主题都有若干章节)列一下吧,看完就知道有多广了:Judgments and Rules,Statics and Dynamics,Total Functions,Finite Data Types,Types and Propositions,Infinite Data Types,Variable Types,Partiality and Recursive Types,Dynamic Types,Subtyping,Dynamic Dispatch,Control Flow,Symbolic Data,Mutable State,Parallelism,Concurrency and Distribution,Modularity,Equational Reasoning。
[《Type Theory and Formal Proof: An Introduction》](Type Theory and Formal Proof: An Introduction),我只看了前 9 章左右,就我看的部分来说,基本在讲 lambda calculus。看引用数可能不是一本特别好的书,但对初学与入门 lambda calculus 的我来说还是很有帮助的。Lambda calculus 保留了核心的函数构造和函数调用,而将其余的语言成分(如自然数)都视为前者的编码。我们会惊叹原来这两条规则已经图灵完备了,任意复杂的代码都可以由其组合出。书里让我印象比较深刻的,一是前几个章节对不同维度的多态的介绍(在第一本书 TAPL 中有提及),以及最终组合出的精美的 lambda cube;二是 lambda calculus 通过统一、简洁的符号,表示出的 term : type : kind : sort 这一条链(如 true : Bool : * : □
),至此(typing 的) term : type
和(kinding 的)type : kind
的许多规则可以统一合并;三是逻辑和类型的对应关系,例如逻辑中的 P => Q
就是 P -> Q
这个函数类型呀。
《The Implementation of Functional Programming Languages》,没有博士学位的 Simon Peyton Jones 的著作,也是一本很老的书了,网上有官方公开的免费版。这本书比较注重实践,读起来没那么抽象。前半部分讲了如何对一个还算丰富的表层语言做类型检查、解糖、翻译到精简的核心语言。如果我没记错的话,核心中的核心是 match 表达式和模式匹配。后半部分讲了图规约(graph reduction)算法和基于图规约的中间表示(IR)和抽象机 G-machine,以及相关的优化算法。这是一种完全不同于栈机(stack machine)的抽象机。Haskell 的编译器 GHC 的实现中大量用到了本书中的思想(毕竟本书作者就是 “GHC 之父”)。
如果想学习函数式的数据结构和算法,那么 Chris Okasaki 的《Purely Functional Data Structures》(这里有一本)和 Richard Bird 的《Pearls of Functional Algorithm Design》(这里有一本)就挺好的。我记得读大学的时候,红黑树的删除(含旋转)操作简直是噩梦。但函数式基于模式匹配的写法,似乎十几行就完事了,理解起来异常简单。当然,我得提醒,函数式其实也没有高明到哪里,它不是万能的(这儿还有一篇文章)。
学写证明的话,最近几年新出的《The Little Typer》或许不错。活到现在还不太会写证明,抱歉。
说了这么多,其实有些大佬的页面就是宝藏。比如这位有三四个博士学位的(好像是物理、化学、计算机等)Oleg Kiselyov 的页面就够大部分学一辈子了。里头都是抽象度贼高的运行速度还贼快的硬核神奇代码,再配上论文。反正我是学不太会。
另外,上面的推荐书籍也看出来了,我不是 Lisp 派。所以就遗漏了《Structure and Interpretation of Computer Programs》、《Essentials of Programming Languages》等圣经。里面有些设计电路的章节也挺让人印象深刻的,确实巧妙。说起来就不爽,计算机理论专业干嘛要教 Verilog HDL 这种语言和用它写电路呢?听说牛津大学就是用 Haskell 写电路的。
2023 年留言:别看了,亏麻了。
2021 年 4 月 16 日
韭零后,新入职不久,有了些积蓄,遂匆匆进入资本市场,加入韭菜大军。前后虽然只有短短六个月,但也感觉经历了不少,特立此贴,以示后人。
写在前面:本文的观点(和数据)主要来自伍志坚老师的《小乌龟投资智慧》和博多·舍费尔老师的青少年性格养成读物《小狗钱钱》,以及雷·达利奥的《经济机器是怎样运行的》(改编的小短片)。
本文结构如下:
不想花时间的普通人应当定投宽基指数基金(白酒是个例外),这些基金在近几年的年平均收益一般都超过20%。例如可以
图:一只标普 500 基金的收益,对比同期沪深 300,大家更喜欢哪个呢?
选择基金表明自己放弃微观上的“择时”,即充分意识到自己就是一棵韭菜,放弃一天交易多次。(场外)基金每天只有一个价格(15:00 的收盘价),而股票的价格每秒都在波动,一天的最低价和最高价能差上百分之几十。毕竟相同的钱,股票一个月就玩没了,基金可能可以玩上个大半年,何乐而不为呢?好吧,正经地说来,
基金经理的选股能力还是比大部分人好的,还会自动帮忙调仓。
有些天价股一手要二十几万(比如茅台),很多人买不起(或者补仓困难),而基金起购点多为100元、10元、1元等,价格亲民。
个人觉得 A 股内幕消息比国外的股市多(虽然在改善),基金公司显然会比普通人更有机会接触到内幕消息,提前买入或者提前跑路。
数据来源:上文提到的伍志坚老师的书中的图8-3
选择指数基金是因为各种花里胡哨的基金或者投资策略在 5 年以及更长期中都很少能跑得过简单的指数型基金。
选择宽基是因为这些指数没有行业限制,所以通常含有市场中市值最大、流动性最好的公司。(例如白酒指数就不是宽指,它只包含白酒,不会包含医疗、芯片、能源等公司;但因为种种原因,这玩意的走势一直挺离谱。)
选择定投表示自己放弃宏观上的“择时”(选择入场时机),即自己不知道什么时候该“抄底”,什么时候该卖出离场。充分意识到自己就是一棵韭菜。
说了这么多,在这里说一下本人买入的基金吧。不敢说是建议,万一碰到个金融危机 N 年后还是亏了会不会把我打死……
我买了
买入原因
首先,我比较认同如下观点:美元与黄金脱钩后,世界经济蓬勃发展,泡沫也越滚越大。2008 年后,各个国家都增发了大量(广义)货币以应对(每一次)危机。一般来说,钱多了会引发通货膨胀(所有物价上升);但从最近 20年看,这些多出来的钱并没有在发达国家引发通货膨胀,而是以泡沫(某一物品价格不正常激增)形式呈现。超量的钱总要有去处的,这些去处在美国很可能是股市、在中国很可能是房市。
因此,
(另外,伍志坚老师在他的书中提到,基金过往的业绩、基金经理的教育程度、毕业院校、甚至是否金融经济专业,长远来看(大于 5 年)对基金的最终收益影响都很小,好像是在 0.01% 这个量级左右。而基金收取的费用(管理费,申购费等)等才是最影响最终收益的。但我没怎么研究这一项。)
图:股灾前夜的中证 1000,可以看到,2015 年 2 月份到 6 月份涨幅超过 100%
图:股灾前夜的沪深 300,2015 年 2 月份到 6 月份涨幅 63%
如果想买基金了
定投不是梭哈,所以肯定不会一次把钱花完的
如果想卖基金了
基金适合长期持有(其实股票也应是这样,如果坚持价值投资的话),所以在卖出前可以考虑一下要怎么使用拿出来的钱
关于股票
关于基金
我开始购买基金和股票只是因为自己不想把工资放在银行定期或者(货币)活期。但也赶巧了,我刚加入资本市场后(2020 年 10 月)没多久,铺天盖地的基金广告就开始席卷地铁站等公共设施,基民、股民数量大增。这说明市场(相对于以前)或许有些过热了,肯定要割一波韭菜的。(遥想 2015 年上证指数好像有 5100 点,而 2021 年 4 月 7 日的上证指数是 3500 点;当时梭哈(孤注一掷)在顶部的人离解套还遥遥无期。)因此我实在是不敢梭哈。
一开始我像逛超市一样,啥基金都买一点。但随后就发现买的其实很同质化,虽然涨的时候不怎么一致,但跌的时候倒是一起跌(抱团股呗),跟只买少量的几只区别没很大。而且啥都买一点,很分散精力,浪费这个时间我认为是不值得的,那样下去即使赚了钱也会输了人生。
再后来我对股票有了兴趣,在里面玩了一玩。那时候韭菜还在玩短线,却刚好赶上上证指数从 3700 点滑坡到 3300 点,亏了 5000 元(10%)左右。也算是知道了什么叫“利好出货”。现在是 4 月,正是出年报、第一季度报的时候,也是爆雷不断。个股还是太危险了,在详细研究(价值投资)一家公司前或者精通 K 线图(所谓的“技术派”)前真的不能投。而且持有个股玩短期很浪费时间,不值得。
再后来我就看了点休闲书和视频,就是文章开头所说的那些。感觉是不看点入门书,真的不能莽撞地进入资本市场和梭哈。
TBC……