Channel 9上有个非常好的介绍Haskell和函数式编程的视频,这个视频是根据Programming in Haskell这本书讲述的。Slides和Codes可以在http://www.cs.nott.ac.uk/~pszgmh/上下载,不过后面几章会和实际的课程内容有出入。此外Learn You a Haskell上的教程,以及Wikipedia上有关Haskell的词条也是非常有用的。
相对于其他的一些Haskell的教程,通过这本书/视频进行学习能够了解Haskell的好处以及设计原理
C++右值
C++11标准引入的是右值引用的概念来方便我们操作右值,但右值的概念是在之前的版本中就有的。在引入右值引用概念后,左右值也被分为左值(lvalue)、将亡值(xvalue)、纯右值(prvalue)。其中将亡值和左值合称为泛左值,将亡值和纯右值合称为右值。
- 左值可以形象理解为可以取到地址的值
比如字符串字面量能取到地址,是左值。 - 纯右值例如整型字面量或者求值结果相当于是字面值或者不具名的临时对象
- 将亡值包括类似
T && foo()
函数返回的右值引用或由std::move
强转来的右值引用。
将亡值属于泛左值,又属于右值。属于泛左值是由于将亡值作为右值引用是具名的,这和纯右值如字面量不一样,所以被视为左值。作为右值是由于将亡值具有可移动性。而将亡值之所以又具名又能移动,是因为它要死了。
这些概念的区分涉及到 Value categories,在这里会有简单讨论。
注意类似 T foo()
的函数返回值是纯右值。在使用右值和移动语义时容易产生下面的问题:
- 右值、右值引用之间有什么区别
- 重载决议中右值引用、左值引用、通用引用有什么区别
- 右值、(N)RVO之间的关系是什么
- 移动语义在哪些地方可以提高性能
诸如此类,在本文中详细讲解。
Leetcode解题报告
寒假没事情,在家里刷Leetcode。这里放的是LeetCode解题报告【更新中】,代码在GitHub上,有些被坑的题目会专门写一篇post。
必须先对Leetcode吐个槽,这复杂度卡的真是魔幻,同样的复杂度C++能过,Python就不能过,而且都是卡在最后两三个样例上(不会就最后两三个大数据吧?)
Leetcode上面有题解,不过有时候很奇怪他们算复杂度的时候会强行令某些操作,比如判断字符串是否相等(Problem 14),std::map
查找元素(Problem 1)的复杂度为1,感觉这并不是很严谨的,后来在Google Codejam/Kickstart的官方题解上也看到类似的算法,只能说这是一种计算方式吧。
在刷Leetcode的时候,取得Accepted通常是容易的,但是如果能够翻翻Submissions里面速度靠前的答案,看看人家是怎么在同复杂度下进行常数优化也是很有必要的。
去除reimage repair恶意广告软件
今天发现自己的Chrome上超链接被篡改了,在点击这样的超链接时,会打开一个独立窗口并跳到某个广告页面然后窗口自动消失,并且扩展程序页面chrome://extensions
打不开了。后来发现这是一个叫reimage repair plus的恶意广告软件导致的
Leetcode4 Median of Two Sorted Arrays
LeetCode第4题,求两个数组nums1
和nums2
的中位数,要求对数复杂度。这个思路很清晰,就是二分。一开始想的lower_bound
比较一波,算一下偏移,然后两段去掉相同数目的元素,构成一个子问题。不过实现的时候被字符串常见的边界情况和上下中位数困住了,想了好久,后来发现其实自己想复杂了,这就是一个求第k个数的问题,直接二分答案就好了。
C++中编译器优化导致的一个问题
今天在MSVC2015上在用for
遍历std::vector<T>
时遇到一个Access Violation错误,关键代码如下
1 | for (auto i = v.size() - 1; i >= 0; i--) |
Fortran数组的C++实现
最近在写CFortranTranslator,一个从Fortran77/Fortran90到C++14的工具,其中涉及到使用C++为Fortran实现一个数组库。
总的来讲,Fortran90的有些语言特性在编写和编译上都让人不是很舒服。比如没有头文件和前置声明,这导致了许多额外的代码和解析工作,比如INTERFACE
块因此而生(但其实也是一个比较好的解决方案,和前置声明也差不多)。又比如Fortran兼容老标准的问题,这个写C++的同学也应该深有同感,C++为了保持和C的linkage做了不少擦屁股的事情,像什么POD、函数指针/函数对象啥的。Fortran老标准中允许隐式声明变量,而且可以通过名字推断变量的类型,而且由于COMMON
块的存在还要兼容一些奇妙的用法。这使得处理变量声明的工作要延迟到处理函数体时。Fortran90还可以直接根据Attribute specification statements给变量加属性(ISO/IEC 1539 : 1991 ch5.2),加上隐式声明的情况,实际上类似于INTENT
、PARAMETER
、DIMENSION
这样的语句需要考虑是生成一段新变量声明还是修改老声明,这同样会延迟处理变量声明的工作,还会要求建立符号表。此外各种impied-do结构,虽然可以完全展开成for循环,但是为了保持Fortran源语句的抽象,最好还是做成一个表达式。还有Fortran77里面的nonblock DO construct非常恶心,从语法上完全无法解析了,最偷懒的办法就是先用start condition给flex开洞,再#define YYLEX
给bison开洞。不过后来token级的continuation迫使我直接手写词法分析了,果然偷懒还是要不得的。
此外Fortran的传参机制也很特别,类似于宏的形式,基于引用,具体类型和限定要在函数体中才能看到。为了能够兼容语义,我全部使用了右值作为参数限定
关于C++元编程的部分可以参考C++模板编程这篇文章中,关于使用flex/bison进行语法分析的部分我放到了flex和bison使用,其他的部分放在这里。
C++中static关键字的用法
C++中static关键字具有很多迥然不同的意义与用途,常在不同的情景下出现。例如声明局部静态变量、声明静态函数、声明类的静态成员。这三种用法的背后分别对应着不同的linkage。本文还将static
与inline
、extern
等存储类指定符进行简单的比较,以期了解C++编译阶段和连接阶段的行为。
C++初始化方式
C++新标准之后对初始化方式有了很多的变动,现在的初始化方式主要可以分为五种来讨论,分别是list initialization、aggregate initialization、zero initialization、default initialization、value initialization。本文根据标准以及cppreference上的相关资料论述了这五种初始化方式,并讨论了POD、成员初始化列表、new关键字等方面的问题。
1 | // Value initialization |