-
在日本考驾照
最近最终把日本驾照考出来了,花了整整五个月。考出来的时候决定一定要写点什么,一方面是记录,另一方面也可以作为其他人的参考。 先说一下,我在国内有驾照,MT,手动挡。在日本重新考一个驾照是考虑到自己距离上一次开车时间很久,自己在国内学完之后也只开过半年,还只能算初学者。而且日本的交通规则和国内的很不一样,日本是少数三个靠左行驶的国家之一。 一部分人可能会选择外国驾照转日本驾照,那你可能需要到驾照考试中心(運転免許試験センター)去考一次。我自己没有经历过,从考试内容来看类似日本“仮免試験”。只知道靠左行驶是不够的,标牌的意思,十字路口的优先顺序都很重要,所以考试前你至少需要几个课时的实际训练以及相关的交通知识。 有些人可能会有国际驾照,日本的法律规定国际驾照只能用一年,之后你要么不开车,要么想办法弄个本地驾照。 我花了五个月学开车,主要是因为上班没时间。大大小小总共六次考试全部顺利通过也花了那么多时间。我以前在国内学的驾照有四次考试 科目一(笔试) 科目二(车上,考场内) 科目三(车上,考场外) 科目四(笔试) 我在日本这边的驾校一路下来有六次考试 仮免前効果測定(学科) 修了検定(実技、仮免試験) 仮免学科試験 卒検前効果測定(学科) 卒業検定(実技) 学科試験(試験センター)
-
英语学习笔记
从去年年初开始重拾英语差不多有一年半多了。最近一两个月开始上在线英语对话,训练自己的口语。现在处于可以开口讲,但是词汇和表达不够的状态。我想分享从几个月前没开口讲到现在能开始讲的期间内自己感受到的东西。 在线英语对话 是一个很好的trigger。在线英语对话比自己对着镜子讲,找非native的朋友讲都要好。原因是你必须讲英语,你在一个受控的环境中。你听得到对方的问题,想要回答对方,用英语回答,这一连串的过程是你以前没有的经验。换句话说,在线英语对话可以帮助你训练思维回路,达到相对自然回答的状态。 回答的范例以及and/so 不是说有了在线英语对话,你就肯定能流利地开始讲英语了。我大概在一个月左右的时候感觉自己可以比以前自由地回答了。一个特点是流畅度比以前有所提升,另外一个特点是不过度使用and/so之类的连词。严格来说,我不认为这是在线英语对话的效果。在这一个月内,我买了一本《SPEAKING FOR IELTS》的书,并且开始上第一课。IELTS Speaking考试的Part 1其实和在线英语对话很像,就是闲聊。重点是书中提供了范例答案。 自己一个人学习时最大的问题是,你没有参照,你不知道对错。我发现范例答案中有and/so之类的连词,但是用法不太一样。不过度使用是一方面,不怎么用可能更贴切一点。口语不是写作,不需要特别明确的连词来指出关系。范例中递进也好,并列也好,其实不需要连词也可以理解。而且你可能会发现范例的主语,包括形式主语会有变化,这可能是英语表达的习惯,也是你必须找实际范例才能看出来和理解的东西。电影,书籍包括新闻都不会很明显地体现这些特点,但是各种口语考试的答案是很好的参考。
-
租房还是买房
最近因为某些原因看了很多买房的资料,但是最终放弃买房决定租房。整个过程中了解了很多买房和租房的知识,现在分享出来希望对各位有用。 首先,我希望站立在一个你和你的另一半能够自由选择的角度看待这个标题。因为丈母娘等原因必须买房的情况不在讨论范围内,这种属于社会属性大于实际居住。另外,一个人居住的话选择面比较广,这里的讨论不一定适合你。 买房和租房哪个更好,如果你尝试搜索的话,会找到很多比较的文章。比较买房和租房各自的好处和坏处。但是同时也有各种外部因素影响你。比如说,不知道是不是国人受丈母娘要求买房买车的影响比较大,到了国外也有优先买房的倾向。还有一些租房的钱不如去买房的说法。整体来说,想要理性分析还是有点困难的。但是如果你把租房当作后备选择,把买房作为主要判断对象的话,决定起来会相对容易一些。 是否需要买房,可以从多个角度来看,比如说 买房有什么好处 因为什么买房 你的计划是什么
-
买了保险
2021年已经过去1/4,在这3个月时间内做了好几件事情,其中一件就是买了保险。 保险对于我们这一代人来说可能没有什么好的印象,不要花钱在保险上,甚至保险等于骗钱。不过,对于大部人一般人来说,保险是少数可以保障我们未来的手段。 死亡保险和医疗保险是两个比较重要的保险。 一般来说,到子女独立之前死亡的话,对于家庭的影响比较大,为了避免意外加一个死亡保险比较好。另外,如果你有住房贷款的话,在日本一般会加入“団体信用生命保険”,死亡之后贷款消失,所以住房贷款可以和死亡保险分开考虑。 日本的死亡保险主要有两种,终身和定期。定期的死亡保险到一定时间会自动停止保障。你可能会认为,到时候你没有挂的话,你付的钱全都归保险公司。事实确实如此,不过你要从保险的本质上来看,保险不是赚钱,保险是出意外时的保障。终身一般会比定期贵很多,但是解约时会返钱给你(定期一般不会)。定期里面还分收入保障型和一般意义上的定期。前者是每个月付款,后者是一次性付款。一般每个月付款的类型相对便宜,后者会贵一些。上面只是一般意义上的分类,具体到产品的时候会有不同,比如说有终身和定期混合的产品等等。
-
2020年总结
2020年对我来说就是在家办公的一整年。幸好自己的工作是程序员,远程办公也没有受到很多影响。而且在这一年中,自己也做了一些事情。 英语学习 年初开始重新学英语,主要是为了以后打算。一开始认为一年左右差不多就能开口讲了,结果只把一本剑桥的150单元的英语语法书《English Grammar in Use》学完了。大学毕业后没碰过英语的状态下,如果你重新开始学英语的话,看来至少要2到3年。 这一年里,个人感觉学习了很多东西,比如说用Anki背单词/词组/句式。发音方面,纠正发音、通过《American Accent Training》学习正确的语调。语法方面,剑桥的语法练习书把以前没弄清楚的,自己没认真学的语法点复习了一遍。 下半年的时候,买了一套四册的《Real Listening & Speaking》(分别对应A2、B1、B2、C1等级,之后称《L&R》)作为口语学习的资料。学习了一册。里面的句式,场景很实用。比电影/电视剧的学习方法感觉好很多。 接下来的一年,除了继续学习《L&R》剩下的三册之外,考虑找一些Online English Talking的课程。 去年这个时候,如果我先选择在线英语对话的话,估计过程和结果都会不一样。比起直接开始学习英语口语对话,个人觉得花时间准备是必须的,语法是一方面,发音的纠正、语调的调整还是需要你自己来做。总体来说,过了一年我还是不能自由的讲英语(日语的话我基本能表达清楚我的想法),但是我觉得一年的回复和准备时间还是需要的。 GTD 同样是年初,我把不怎么用的Remember The Milk(下面称RTM)认真地用了起来。配合GTD(Get things done)的理念,分Inbox,Next Actions,Projects,Later等列表管理任务。效果很明显,自己的学习和工作比以前更加有效率了。 比如说工作上,经常有等待其他人的情况,专门建立一个Waiting列表或者加waiting的tag,这样你就可以知道现在这个项目,这件事情进行了什么程度。用期限和next-action的tag管理一个接下来要做的工作的动态列表,这样你第二天就不用回想昨天做了什么,直接看这个列表,选择优先级高的开始做就行了。 RTM毕竟是个收费软件,如果你不熟悉GTD的话,建议你可以用其他软件尝试一下GTD,然后再来试用RTM。总的来说,只要你理解了GTD的想法,软件是其次的,选择你习惯的软件就行。 出书 2020年中旬的时候,自己的书终于出了。从开始写书到出书花了一年多。很明显不能靠这个吃饭,不过作为副业可以考虑。2020年因为开始学习英语,自己没有多少精力写项目了,也不太可能继续出书。这本书作为自己的输出的一部分,加到了LinkedIn上,算是自己的宣传。 文件云端化 2020年下半年的时候,把自己的笔记本里的文件一点一点上传到云端。自己的照片也是。从各个设备收集,放到了云端。做这件事的主要原因是,考虑到日本是自然灾害比较多的地方,哪天因为地震家都没了的话,自己的重要的纸质文件也好,电脑里的文件也好都有可能消失。所以把重要纸质文件扫描并且加密保存,电脑里的文件云端化。 以上就是我的2020年。2021年准备继续自己的英语学习,用GTD管理工作和学习。最后希望2021年疫情能够结束。
-
使用Raspberry Pi转发国内短信到Slack
人在国外,虽然大部分时间不需要国内的服务,但是一旦需要用国内的公共服务或者银行相关的服务,基本每次都需要输入短信验证码。如果你有一台闲置的旧手机,一直插着电源,再装个短信转发服务理论上可以满足需求。我自己没试过,因为在那之前我的旧手机太旧了,型号Galaxy S2,电源键反应不良,不插电电池容量估计也就撑个几个小时,随时都有可能报废。另外还有一台旧手机不知道为什么无法接入网络和接收短信。如果为了“闲置的旧手机”再去买一台新手机或者中古手机非常不划算,想了想还是用自己闲置的Raspberry Pi做一个短信转发,一方面网上资料比较多,另一方面不用担心电池的问题。 以下是最终成果
-
随笔 – 英语学习
好久没写博客了,也不是说完全不碰技术类的东西,所以没写博客。只是自己差不多一年都在学英语,仍旧在学习中感觉没有多少可以转为博客。 不过整体来说,到现在为止10个月感觉自己的英语和以前有很大的不同,语法比以前有自信了,发音也比以前好很多。到年底的时候,差不多自己的那本《English Grammar in Use》中级学完,开始专心学习《Listening and Speaking》训练自己的口语。明年开始估计就可以找在线的英语对话开始练习了。 一开始我给自己定了一年学习时间,现在看来语言学习真得是一个漫长的过程,运气好的话两年估计才可以达到我的目标。我的目标是能够用英语对话,就像我现在能够用日语对话一样。我的日语是N1,英语的话用欧洲那个ABC标准估计口语要在B2(中级上)。不管怎么说,都已经坚持10个月,再多坚持1年也不会太难,希望自己能够达到自己的目标。
-
理想的网络程序架构
在写完自己的Raft算法实现xraft之后,对自己欠缺并发和网络程序方面的知识非常有痛感。之后自己找了很多并发程序方面的资料,比如《多核编程艺术》,相对系统性地学习了并发数据结构的知识。其中比较中意STM(Software Transaction Memory)技术,因为实际的程序中对于共享数据的访问模式不会像典型并发数据结构那么简单,这时候,拆分数据,并且用STM来访问的话可以间接解决并发访问的问题。 到此为止,我一直认为自己是并发程序方面有所欠缺。转变发生在今年年初,自己看到别人写的一篇实现Bittorrent Client的文章,自己也想实现一下。个人在很早以前,就对实现Bittorrent Client很感兴趣,但是当时自己能力只够解析Torrent文件。 在实现Bittorrent Client时,自己有意识地把能够并行执行的代码并行化,并且尝试使用Kotlin的coroutine来写。使用Kotlin的coroutine而不是现在貌似很火的Golang的goroutine来写主要还是因为自己不喜欢Golang的运行模式。Golang的runtime决定了goroutine怎么执行,用户无法直接介入。个人认为应用程序需要一种flexible的机制,能够根据工作负载决定使用基于线程的还是基于work-stealing的coroutine的运行模型。Golang帮你做了决定,在错误处理上也帮你做了决定,有强烈的设计者(C语言)的风格。 回到主题,相比之下,Kotlin的coroutine提供了自定义运行模式的方法,我可以强制要求某些coroutine只在某个线程中之行,利用thread confinement避开锁或者其他同步机制。而且Kotlin的coroutine提供了结构化的coroutine,能够简化复杂的异步编程场景。比如说,我先异步访问服务A,结束之后在并行访问多个服务B,全部完成后再访问服务C,Kotlin里面不需要额外的同步类,只需要使用coroutineScope,launch,async/await就可以实现。整体来说,Kotlin的coroutine非常适合复杂的业务场景,用一个类似DSL的方式类型安全地编写异步/并行编程的代码。 不过我在用Kotlin的coroutine编码的时候,发现一个问题,准确来说这和coroutine没有关系,是你的网络程序该用什么架构?习惯了Web后端的线程模型,NodeJS的单线程模型,UI程序的单事件模型之后,对于网络程序,感觉没有一个确定或者通用的模型。coroutine是一个模型么?个人认为coroutine不是一个模型,coroutine准确来说是一种实现,运行模式的一种实现。你可以用基于线程的运行模式,也可以使用coroutine+work stealing的运行模式,这只是两种不同的实现。对于网络程序来说,你的工作负载决定了你适合哪种运行模式。 一般人可能会按照IO多,还是CPU多来分。很明显CPU工作多的程序不希望频繁切换CPU,而IO非常多的则可以在IO等待时运行其他任务避免浪费CPU。现实中的程序,往往是两者兼具的。在定性你的程序之前,另外一个注意的是,运行模式会影响到你的并行程序的写法。程序调度、共享数据访问、线程管理其实是互相关联的,不只是网络程序,非单线程程序都需要考虑这个问题。 之前也提到通过thread confinement可以避免使用锁,这其实就是一个通过程序调度解决共享数据访问的例子。假如一个语言把程序调度和线程管理都帮你做掉了,那么你必须自己解决剩下的共享数据访问问题。Golang提供了锁和间接解决问题的工具channel,Erlang禁止共享数据访问并且提供了Actor方案。 语言提供的解决方案,往往存在可以优化的空间。比如说我有一个只有单个线程会更新的数据,由于其他线程只会读,所以不需要锁,也不需要发送消息。从另一个角度来说,基于通信的方案可以解决更复杂的问题,但是简单的场景可以使用更简单和高效的方案。另外,基于通信的方案对于一次要求多个数据更新的场景支持并不好,有些人会退回到粗粒度锁,但更好的方案是STM等。 总体来说,网络程序也好,一般的多线程程序也好,需要一个灵活的、能够最大化性能,以及解决共享数据访问的整体方案。个人在尝试了多种方案之后,觉得类似Actor,或者说从SEDA(staged event driven architecture)出发的方案比较合适。SEDA把工作负载按照步骤切分,提供了并行执行的方向,间接地解决了共享数据的问题。你可以把SEDA中的Stage等同于Actor,只不过Erlang用coroutine来运行Actor。这里面的重点不是coroutine,而是Actor在收到消息之后,顺序执行的这点。所以Actor是类似Process的东西,而在Process中执行的指令满足process confinement,理论上不会出现多线程访问下的数据竞争问题。其次Actor额外还有父子结构,自我替换,异常时的处理策略等等。其中父子结构模仿了进程的父子进程,可以用于动态子任务生成,与父Actor在不同逻辑线程上执行,提高并行度。 从解决方案的完整度上来看,Actor确实可以作为一个参考架构。为了进一步提高性能,你可以放宽Actor要求,允许Actor收到消息后乱序执行(或者说没有Queue)。还有就是Erlang是一门函数式语言,只有不可变的变量,假如你用其他语言,可以使用语言本身的内存模型来小心地共享部分变量,减少Actor之间的消息。 个人其实也是这么做的,在几次重写之后,用Kotlin写了一个简单的Actor运行时,使用Actor架构的变体来同时解决并发数据访问和运行模式的选择。整体上来说,不用考虑背后是用线程还是coroutine,并发数据访问也很容易,应用层面不需要锁。到现在为止,应该算是我个人认为比较理想的网络程序架构了。
-
RxJava与常见控制结构
在编程语言里面,控制结构是比较基础的一块内容。常见的是顺序,分支和循环。部分语言有try catch finally结构等。在使用RxJava时,如何对应既有的控制结构,或者说既有代码如何转换成RxJava的代码,是学习RxJava时必须熟练掌握的东西。 顺序结构 在三种常见控制结构里面,顺序其实是比较难的一种结构。比如说以下两行顺序代码 a = operation1() b = operation2(a) 和另外一类顺序代码 a = operation1() b = operation2() operation2是否需要上一行操作的结果对如何转换成RxJava的代码有很大的影响。 对于第一种有依赖关系的代码来说,需要使用flatMap Single<A> operation1() {…} Single<B> operation2(A a) {…} operation1 .flatMap(a -> operation2(a)) 因为有依赖关系,operation2不能与operation1同时进行。 相反,如何operation2不依赖operation1的结果的话,理论上可以同时执行。 比如说上面第二类顺序代码 Single<A> operation1() {…} Single<B> operation2() {…} Single.zip(operation1(), operation2(), (a, b) -> {…}) 这里通过zip让两个操作同时进行。这是一般顺序代码难以表达的结构。 不过,仍旧存在一类代码,虽然两个操作没有直接依赖关系,但是隐藏着第一个操作失败之后,第二个操作不应该执行的错误依赖要求。对于这类代码,就不应该使用zip让两个操作并行,你需要退回flatMap的方式。 分支结构 分支结构相对简单,比如说最常见的单个if if(condition) { // do something…
-
使用RxJava把你的异步调用同步化
每周的定期博客。 本周个人认为的亮点之一是在测试中使用RxJava,把异步调用同步化。让代码变得“清晰”了许多。 如果你使用基于异步的编程方式的话,肯定会碰到一个如何测试的问题。最简单的比如说 AsyncResult<String> foo = asyncService.foo(); 或者基于回调方式的异步代码 asyncSerivce.foo(asyncResult -> { // do stuff }); 前一种还好,后一种在多个异步调用时很容易碰到callback hell的问题。 A.foo(a -> { a.bar(b -> { b.baz(c -> { }); }); }); 比如说上面这种嵌套比较深的代码。一种解决方法是类似JavaScript的Promise的链式调用。 A.foo() .andThen(a -> a.bar()) .andThen(b -> b.baz()) 但是链式调用的一个问题是,在方法调用之间是非简单类型的依赖关系时,你需要一些中间类,而且调用顺序有所变化时,中间类也必须随之改变。比如说上述代码中bar依赖A,baz依赖A与B时 A.foo() .andThen(a -> a.bar().success(b -> (a, b)) .andThen((a, b) -> a.baz(b)) 相比之前的写法会显得繁琐。对此,你可以改用JavaScript的async/await,完全把代码改成同步风格,免去些中间类的需要。 async/await是协程的一种实现,但是Java没有协程,该怎么办?方法很简单,利用类似Future的get把代码同步化就可以了。特别是在测试代码中,异步代码转开成同步代码并没有太大问题。相反的,同步风格的测试代码更好理解。 于是在测试代码中可以这么做 把回调式代码转为Observable事件源 使用Observable的blockGet,blockAwait转换为同步代码 Observable<A>…