据说这个游戏可以很快的增加你的内存

今天的主角名叫 N-back,起源似乎是某种心理测试,然后碰巧有人发现它碰巧和大多数智力游戏只能提高你在那一个游戏上的表现不同,N-back 的作用可以转移

关于 N-back,更多的细节可以看这里:N-back FAQ(作者貌似是个大牛),文中提到几点挺有道理的,比如说不管内存(Working Memory)和 IQ 是否有直接的关系,但至少对程序员来说,快速的记住几个变量及其类型、几个函数及其原型什么的可以很大的提高工作效率——也就是说内存这东西还是挺重要的。这个,是当然的了

下面提一下 N-back 怎么玩:

首先,N-back 中的 N 是一个变量,当 N==1 时,你需要判断上一个图形和当前的图形是不是一样的;当 N==2 时,你就需要判断上上个图形和当前的图形是不是一样的,依此类推。

当然,上一句提到的图形也可以不仅仅是图形,还可以是字母、数字、声音等各种刺激;而一种称作 Dual N-back 的变种则是同时施加两种刺激,你需要分别判断它们各自是否和之前匹配。

下面是我乱写的 Single N-back:

(BTW 我显然不会去考虑 IE 用户的感受,读者您自重吧)

其实我觉得 Dual N-back 更好玩一些啦,不过想不到有什么好的操作方式,所以没加上来(用 A,L 操作也实在太囧了吧!),iPhone 上的 IQ boost 的 2-back (Dual) 我已经可以玩到全对了,哈哈,炫一下。

意识流

一晃又有好久没更新博客了,唉

 

由于想起来“啊!得要开始写论文了!”的时候已经非常晚了,所以干脆把之前定的题目给改了,用写过的五子棋程序作为毕业设计,写了三四十页文字翻译了二三十页文字就差不多了,然后再使劲的折腾格式,然后再答辩,两个星期就过去了。

嗯——话说答辩这事儿比想象的轻松多了。

 

和更新博客一起被忘记了要更新的还有某个说了要重写的五子棋AI,还有某个待修复的把普通的后缀树当成广义后缀树使用的bug,还有作为程序员不折腾出一个就会死的属于自己的语言,等等。再说吧。

 

对了,回到学校发现女士们都一律小清新了,叹气,其实我挺喜欢那种样子的,可是大家都一样就没意思了。

千万不要有人把 Enya 和 Cara Dillon 叫做小清新就好。

 

最后,看点花花草草吧

water

grass

flower

flower

哦对了,我的法线贴图做好了

看看这光影的变化,多漂亮啊:

其实那些凹凸并不在模型上,而只是通过一张图保存了平面的法线方向。

利用法线贴图上的法线信息计算光照的时候需要注意的是得要把它的坐标系(横着是 red, 竖着是 green, 立在平面上的是 blue)转换到空间上对应平面的坐标系中 —— 或者反过来,把光线和视线转换到法线平面的坐标系中(我就是这么做的),然后再那啥。

提一下,从网上搜到的信息来看这个的计算貌似挺复杂的,但是考虑线性变换其实也就是线性方程而已,从法线贴图的坐标系转换到空间坐标系,当前正在渲染的点所在的空间三角形的贴图的 u 坐标的正方向为 a,法向量为 n,不考虑位移(为什么可以不考虑位移?自己想),我们可以列出这么几个式子:

z = cross(a, n);

(1,0,0) -> (a.x, a.y, a.z)
(0,1,0) -> (n.x, n.y, n.z)
(0,0,1) -> (z.x, z.y, z.z)

 

有没有恍然大悟的感觉?

 

没有?再提示,设从法线贴图的坐标空间转换到空间三角形的坐标空间的矩阵为 M, 那么有

1,0,0            a.x, a.y, a.z
0,1,0  *  M ==  n.x, n.y, n.z
0,0,1  z.x, z.y, z.z

好了,点到为止。

 

俺用的漫反射贴图是这样的:

漫反射贴图

法线贴图:

法线贴图

高光贴图:(虽然用了而且计算了,不过光源放的位置似乎不太恰当以至于完全看不到高光的效果,囧~)

高光贴图

可执行文件:

binary

 

源码……还是不给,因为写得丑了……

 

PS. 想要运行着玩玩看的话你得把上面三个贴图和这个程序保存在一起,如果你有更靠谱的贴图当然更好(分别保存为 tex.png, tex_NRM.png, tex_SPEC.png)。

PPS. 在学 DX,所以上面用到的线性变换也是假设向量是行向量,所以对向量的变换就是用行向量左乘矩阵,不过换个方式用矩阵左乘列向量区别也不大。

PPPS. 扫了几本 3D 数学方面的书,发现还是这本比较靠谱: http://book.douban.com/subject/1400419/

PPPPS. 非常感谢老胡当年让我感受到了线性代数的好玩之处,不然现在在公司里可要遭鄙视了。

PPPPPS. 我写的 3dmath.h,如果你有兴趣看的话——含有一个向量类一个矩阵类,若干无趣的函数,唯一比较有意思的东西是透视和投影。

PPPPPPS. 好几次看见自己拍的照片,突然想到,哇,这阴影好逼真啊。

恭喜我吧,本博客今天开始得要翻墙才能围观了

倒不是得到了 Game For Windows 的特别关照,只是那个之前提供代替 ghs.google.com 域名解析的好心人干不下去了,所以算了,我也懒得折腾,觉得我这博客有点儿意思的人你就订阅一下或者想办法翻墙吧,获取信息毕竟是基本的生存技能啊。

另外我的 163 博客也可能会重新维护吧,说起来我也算是个网易的人了~另外那里贴照片什么的实在是方便(可是文件怎么办,JS 写的 demo 怎么办……),还不用担心访问量太大(这个可能是想多了,囧,不过某次某游戏被小众软件推荐的时候还真差点爆了当天的流量)…… 那些都再说,反正在网易可以无障碍访问 Google 的系列服务(以及 Twitter 等),我自己继续在这儿自娱自乐还是不会嫌麻烦的~

 

update:

  又找到一个非常不错的免费服务,可惜我都不敢表扬它,怕一旦知道的人多了它就死了……唉……

 

无营养两则

1. 最近在补 3D 知识,上周花很大的精力试着手动逐像素渲染 3D 图形,挺好玩的:

老大的要求是高光贴图和法线贴图也是要的,我也做出来了,只不过几个小时之前突然意识到一个非常重要的坐标转换没有做,所以那个法线贴图的结果其实很不靠谱,代码先不放了,省得丢人,等把法线贴图做对了再说吧。

这是阉割版的程序 —— 如果你有兴趣围观的话:rending.7z

哦对了,这里用到的数学运算俺也都自己实现了一遍,等有时间了会整理一下的,毕竟这个其实挺好玩。

 

 

2. 觉得 mataball 这东西挺有意思,结果就整出了个奇丑的:

任何试图画出有两个眼睛一个嘴巴的面部的尝试都会得到一个青蛙

程序和代码在这儿:metaball.7z

圈圈们默认是会动的,如果你只是想画青蛙,把 render 函数的第二个参数设为 0 就好了。

BTW Fast inverse square root 真是威武,有多威武你自己试试就知道了。

 

自己编译了一下 SDL

其实我只是想在 SDL 中使用 png 图像,但又不愿意加上 SDL_image.dll, libpngXX.dll, zlibXXX.dll 这大一堆东西,想起不知道什么时候见到的一句让我深以为然的话:糙快猛乃素王道,于是俺就把 SDL, zlib, libpng, SDL_image 分别编译了一遍,然后把它们的 .o 文件都放在一起(除了 SDL_win32_main.o),再然后 gcc --shared -o SDL--.dll *.o -lgdi32 -lwinmm 就成了。(怎么样,够暴力吧!)

SDL_image 有问题,需要改:

72a73,74
> #include <pngstruct.h>
> #include <pnginfo.h>
350c352
<       if ( setjmp(png_ptr->jmpbuf) ) {
---
>       if ( setjmp(png_ptr->png_jmpbuf) ) {

以后自己做点什么游戏原型,或者小游戏,或者寻路算法之类的东西时,可能就会用得上它。

 

你也需要吗?点这里: 动态链接库 静态链接库

 

PS. 这几个玩意儿的授权真乱,一个 GNU Lesser GPL,一个 GNU Library GPL,另外两个是自己的协议;简单的读了一下,不出意外的话和闭源程序动态链接起来应该都是不会有啥问题的,不过另一方面,混合起来之后应该怎么发布还真不知道,所以,我没有发布任何东西,上面的链接纯粹是你的幻觉。

用 hough 变换检测抛物线

告诉你某张图中很可能有某个东西长得很像开口向上的抛物线,想把它检测出来,嘿嘿,看上去很难吧?

其实呢,这种事情很适合用 hough 变换 来做:

假设抛物线方程为 y = a ( x - b )2 + c

先把图像边缘检测出来,对于图像边缘上的每一个点 (x,y), 可以有很多条抛物线穿过它,即有很多个 a, b, c 可以使上面的方程成立,于是, 你很暴力的把这些 a, b, c 都记下来! ,对于每个 x,y 点都记下一次,然后看看哪些 a, b, c 的组合被命中的次数比较多就好了。

当然,上面的那个过程有不少地方是可以优化的,比如说对于图片,b 和 c 肯定是整数值,而且 b 很可能在比较偏中心的位置,而 a 的取值范围可以预先设定,比如说如果你想检测下巴,保守的说 a 肯定就是小于 1 的(不然那下巴也太尖了吧!),如此这般等等——而 OpenCV 中用 hough 变换检查圆的代码中显然还有些其他的不那么容易理解的优化,反正我也用不着把它做得很高效,就没有研究了。

下面上图:

(图片均来源于 pixdaus.com ,如果碰巧它侵犯了您的版权,请与我联系)

 

再上代码——

 

(你确定要看吗??!!)

 

 

(警告!魔数出没!)

 

 

 

(警告!没有注释!)

 

 

 

 

(警告!诡异的变量名出没!)

 

 

 

 

 

(警告!你的大脑可能因为看到这种乱七八糟的代码受到永久性的损伤!!)

 

 

 

 

 

 

(你确定依然要看吗??!!!!)

 

 

 

 

 

 

 

// y = a/150*(x-b)^2+c;
struct Parabola {
    int a;
    int b;
    int c;
    Parabola():a(0),b(0),c(0){}
    Parabola(int a, int b, int c):a(a),b(b),c(c){}
    Parabola(Parabola const& p):a(p.a),b(p.b),c(p.c){}
};
struct Record {
    Parabola para;
    int      count;
    Record():para(),count(0){}
    Record(int a, int b, int c, int cnt):para(a,b,c),count(cnt){}
};
struct RecordCmp {
    inline bool operator()(Record const& a, Record const& b) {
        return a.count>b.count;
    }
};
std::vector<Parabola> detectParabolas( cv::Mat const& img,
                                       int minA=-1,
                                       int maxA=-1,
                                       int minB=-1,
                                       int maxB=-1,
                                       int minC=-1,
                                       int maxC=-1 )
{
    cv::Mat edges;
    // cv::imshow("debug", img);
    // cv::waitKey(3000);
    cv::Canny( img, edges, 50, 150 );
    cv::Mat_<uint8_t> img_(edges);
    // cv::imshow("debug", edges);
    // cv::waitKey(3000);
    int ***acc = 0;

    if(minA < 0)
        minA = 1;
    if(minB < 0)
        minB = img.cols/4;
    if(minC < 0)
        minC = img.rows/4;
    if(maxA < 0)
        maxA = 60;
    if(maxB < 0)
        maxB = img.cols/2+img.cols/4;
    if(maxC < 0)
        maxC = img.rows;

    acc = new int**[maxA+1];
    for(int i=0; i<maxA+1; ++i) {
        acc[i] = new int*[maxB+1];
        for(int j=0; j<maxB+1; ++j) {
            acc[i][j] = new int[maxC+1];
            for(int k=minC; k<maxC+1; ++k) {
                acc[i][j][k]=0;
            }
        }
    }

    printf("calculating ...\n");
    int percent=0;
    for(int y=0; y<img.rows; ++y) {
        for(int x=0; x<img.cols; ++x) {
            if(img_(y,x)) {
                for(int a=minA; a<=maxA; ++a){
                    for(int b=minB; b<maxB; ++b) {
                        int c=y+a*(x-b)*(x-b)/150;
                        if(c>=minC && c<=maxC) {
                            ++acc[a][b][c];
                        }
                    }
                }
            }
        }
        int p=int(round(double(y)/img.rows*100.));
        if(p!=percent) {
            percent = p;
            printf("> %02d%%\n", percent);
        }
    }
    printf("done!\n");

    sizedpq<Record, RecordCmp> pq(3);
    for(int i=minA; i<=maxA; ++i) {
        for(int j=minB; j<=maxB; ++j) {
            for(int k=minC; k<=maxC; ++k) {
                pq.insert(Record(-i,j,k, acc[i][j][k]));
            }
        }
    }

    for(int i=0; i<=maxA; ++i) {
        for(int j=0; j<=maxB; ++j) {
            delete[] acc[i][j];
        }
        delete[] acc[i];
    }
    delete[] acc;

    std::vector<Parabola> res;
    for(int i=0; i<3; ++i) {
        res.push_back(pq[i].para);
    }
    return res;
}

上面那段代码最诡异而需要说明的地方是 a ,我希望检测的是开口向上的抛物线,于是在计算机图像的坐标系统下 a 的取值应该是负数,但作为数组下标时它当然得要是正数,所以这里多绕了几个弯,下次我自己看到肯定也会忘记的,不过还好这算法相当简单,下次需要用到我肯定更愿意重写。

sizedpq 类是俺以前做马赛克拼接图像时折腾出来的一个固定大小的优先队列类,其实设计得并不好,不过接口挺好用,所以.. 就拿来用了。

 

PS. 速度与图像边缘数量关系很密切,不过通常情况下处理一张上面展示的那种大小的图像也就需要 3 秒钟左右吧,其实还可以接受。

PPS. 人果然还是逼出来的啊,今早老宋很客气的问我节日快乐,弄得我很不好意思的在两个小时之内把这个做好了……

PPPS. 上周末和室友去西湖边逛了一圈,在柳浪闻莺景区里,我刚拍完一张照,发现旁边飘过了一个长发白衣飘逸出尘的美女背影,我说哇美女,他说其实不是美女我已经观察很久了...... 唉,男人。

PPPPS. 谁能教我玩 DotA 啊!!!

还是流水帐加意识流

1   来到网易实习

这些天我一直在后悔一件事情,就是还没看保密协议上面都写有些什么就把它给签了………而且到目前为止我还不知道上面写了些什么。

嗯,透露一下下肯定不是商业机密的事情吧,网易的伙食还不错。

2   晒晒我的 vimrc

分到电脑的那一天花了一个上午装各种软件,其中最耗精力的要数 VIM 了,自己的 vimrc 没有随身带着,又考虑到里面塞满了各种从网上看到的看上去很好玩但实际上一万年用不到一次的设置,所以干脆自己重新写了一份,里面全是每天都会用到的东西,不长:

set nocompatible
source $VIMRUNTIME/mswin.vim
behave mswin

if has("gui_running")
  colo Ifs
  set gfn=consolas:h11
  set go-=m
  set go-=T
  set cursorline
else
  colo blue
  set nocursorline
endif

syntax on
filetype on
filetype indent on
filetype plugin on

map  <space> :
map  <C-j> <C-w><down>
map  <C-k> <C-w><up>
map  <C-h> <C-w><left>
map  <C-l> <C-w><right>
imap <C-j> <down>
imap <C-k> <up>
imap <C-h> <left>
imap <C-l> <right>

let mapleader=";"
map <leader>w :w!<cr>
map <leader>q :close<cr>
map <leader>[ <C-o>
map <leader>] <C-]>
map <leader><space> <C-w>

map <leader>j :bn<cr>
map <leader>k :bp<cr>
map <leader>bd :bd<cr>

map <leader>tn :tabnew<cr>
map <leader>l  :tabn<cr>
map <leader>h  :tabp<cr>
map <C-t>      :tabnew<cr>
map <C-tab>    :tabn<cr>
map <C-S-tab>  :tabp<cr>

map <silent> <leader><cr> :noh<cr>

map <leader>tl :TlistToggle<cr>
map <leader>gf :NERDTree<cr>

set history=50
set autoread
set ts=4
set expandtab
set smarttab
set shiftwidth=4
set autoindent
set smartindent
set cindent
set scrolloff=5
set wildchar=<Tab>
set wildmenu
set cmdheight=1
set ruler
set nu
set lz
set hid
set whichwrap+=<,>,h,l
set ignorecase
set incsearch
set magic
set showmatch
set hlsearch
set laststatus=2
set statusline=\ %P\ %F%m%r%h\ %y\ %w\ \ CWD:\ %{getcwd()}%h\ \ Line:\ %l/%L:%c
set showmatch
set showmode

其中 <space><leader> 的设置照目前看来貌似很非主流,但是 <space> 不映射成 : 实在是可惜了,毕竟向下滚动有 j 就够了,而且 j 很好用,把 <space> 映射成 : 可以在敲命令的时候感觉十分痛快;另外 ; 就在小拇指上面,用来敲快捷键再方便不过,默认的用于重复 f, t, F, T 的操作也是基本上用不到的,所以,嗯……

这个设置没考虑到 Linux 下的情况,不过除了 gfn 之外其他应该没什么需要改动的吧。另外 Ifs 主题是我在 lettuce 主题的基础上改的,自己喜欢而已。

插件装得不多,因为发现插件多了启动会慢:

  • bufexplorer
  • c
  • mark
  • matchit
  • NERD_tree
  • supertab
  • taglist

c.vim 要修改一下,它有个 <leader>lcs 的映射,除了让我的 <leader>l 响应变慢之外啥用途都没有,罪过很大。

——然后用起来就很舒服了。

3   Qt,各种吐槽

最新版本的 Qt 光是安装文件竟然就有 1.5 GB !!! 它完全疯了!!!

有段时间特别喜欢 Qt,因为它的每个类都显得非常漂亮,比如说字符串类里面会提供宽字符和窄字符的转换,再比如说 QImage 类光是构造函数就有这么多种:

  • QImage ()
  • QImage ( const QSize & size, Format format )
  • QImage ( int width, int height, Format format )
  • QImage ( uchar * data, int width, int height, Format format )
  • QImage ( const uchar * data, int width, int height, Format format )
  • QImage ( uchar * data, int width, int height, int bytesPerLine, Format format )
  • QImage ( const uchar * data, int width, int height, int bytesPerLine, Format format )
  • QImage ( const char * const[] xpm )
  • QImage ( const QString & fileName, const char * format = 0 )
  • QImage ( const char * fileName, const char * format = 0 )
  • QImage ( const QImage & image )

不但功能上一应俱全,而且甚至不需要任何文档,一眼看上去就能知道每个函数是怎么用的。

让人不爽的还是 C++ 的二进制不兼容问题,上次做的一个小实验清楚的说明了想让不同的 C++ 编译器生成的二进制程序共用一个 C++ 类实在是很难的事情;而更早之前的经验表明即使是同一个编译器,不同的版本之间也不见得可以兼容,坑爹啊,也就是说你根本提供不了一个统一的运行时环境,写了个好玩的程序想发给别人装 B,只有每次都把一堆几十 MB 的 dll 和自己几 kb 的程序放在一起才行...这不是比 .Net 还过分吗...

还有,很大程度上托无所不能的 template 的福,C++ 编译的那速度... 上次编译 Qt 4.6 花了 4 个小时左右,后来我专门统计了一下,不算文件头注释和空行,Qt 4.6 一共有 2362083 行代码[1],按 4 个小时来算的话,那么编译+链接速度平均就是每秒钟 164 行了,还有什么语言能比它更慢一点的!!

怎么好像在对 C++ 吐槽……

……都怪 Qt 太大了, FLTK 这种 kb 级别和谁都能静态链接在一起的图形库,用 C++ 我当然没意见,否则您丫每隔一两月更新一次,每次都扔给我好几 G 的东西,还让我以前的程序面临死翘翘的危险,太过分了。

4   乱七八糟

今天在 cnBeta 上看到一句话 “别人一开源,我们就自主研发了。” 笑死了

搞ACM的你伤不起[2]” - 一看就知道是大牛手笔

[1] 包括自动生成的 moc 文件夹下的
[2] 墙外 其实不在墙外... 作者竟然是同事的同学,世界真小

流水帐+意识流 [2]

1. C++ 很变态

去网易面试了,一个 C++ 上的问题把我难到了:“多重继承的时候子类是不是要保存每个父类的虚函数表?”

我回答 “通常是的,但虚继承的时候好像 %#@$%#^&*” —— 其实我就记得虚继承的时候情况很有点乱,忘了究竟有多乱。

回来之后做了个实验:

#include <iostream>
#include <iomanip>
#ifdef _MSC_VER // 老兄,特立独行的永远是你!!
typedef unsigned int uint32_t;
#else
#include <stdint.h>
#endif

using namespace std;

class A
{
public:
    uint32_t x;
    uint32_t y;
    A():x(42),y(24){}
    virtual void f(){
        cout<<"A::f()    [this="<<this
            <<"]; this->x="<<x<<"; this->y="<<y<<";\n";
    }
};

class B:virtual public A
{
public:
    B():A(){}
};

class C:virtual public A
{
public:
    C():A(){x=21,y=12;}
    virtual void f(){
        cout<<"C::f()    [this="<<this
            <<"]; this->x="<<x<<"; this->y="<<y<<";\n";
    }
};

class D:public B, public C
{ 
public:
    D():A(),B(),C(){}
};

template<class T>
void showDetail(T const& v)
{
    int s=sizeof(T)/4;
    uint32_t const* p=reinterpret_cast<uint32_t const*>(&v);
    cout<<"  +-------------+\n";
    for(int i=0;i<s;++i) {
        cout<<"  | "<<setw(11)<<p[i]<<" | ";
        if(p[i]>10000) { // suppose it's a pointer
            cout<<" -> "<<*reinterpret_cast<uint32_t const*>(p[i]);
        }
        cout<<endl;
    }
    cout<<"  +-------------+\n";
}

int main(){
    A a;
    B b;
    C c;
    D d;
    cout<<"a:\n";
    showDetail(a);
    cout<<"b:\n";
    showDetail(b);
    cout<<"c:\n";
    showDetail(c);
    cout<<"d:\n";
    showDetail(d);
    return 0;
}

为的是看看虚继承的时候到底发生了什么,结果又是大出意外,GCC, M$VC, DigitalMars 编译出来的竟各不相同:

GCC

a:  +-------------+
    |     4666904 | -> 4324460
    |          42 |
    |          24 |
    +-------------+

b:  +-------------+
    |     4666924 | -> 0
    |     4666936 | -> 4324460
    |          42 |
    |          24 |
    +-------------+

c:  +-------------+
    |     4666956 | -> 4324764
    |     4666972 | -> 4631892
    |          21 |
    |          12 |
    +-------------+

d:  +-------------+
    |     4666988 | -> 4
    |     4667000 | -> 4324764
    |     4667016 | -> 4631892
    |          21 |
    |          12 |
    +-------------+

M$VC

a:  +-------------+
    |     4305572 | -> 4198672
    |          42 |
    |          24 |
    +-------------+

b:  +-------------+
    |     4305668 | -> 0
    |     4305664 | -> 4198672
    |          42 |
    |          24 |
    +-------------+

c:  +-------------+
    |     4305632 | -> 0
    |           0 |
    |     4305628 | -> 4217088
    |          21 |
    |          12 |
    +-------------+

d:  +-------------+
    |     4305684 | -> 0
    |     4305632 | -> 0
    |           0 |
    |     4305680 | -> 4217088
    |          21 |
    |          12 |
    +-------------+

DigitalMars

a:  +-------------+
    |     4448636 | -> 4205756
    |          42 |
    |          24 |
    +-------------+

b:  +-------------+
    |     4448640 | -> 0
    |     4581960 | -> 5856153
    |     4448636 | -> 4205756
    |          42 |
    |          24 |
    +-------------+

c:  +-------------+
    |     4448660 | -> 4205896
    |     4448648 | -> 0
    |     4448656 | -> 4203244
    |          21 |
    |          12 |
    +-------------+

d:  +-------------+
    |     4448660 | -> 4205896
    |     4448672 | -> 0
    |     4448664 | -> 0
    |     5838868 | -> 4500440
    |     4448680 | -> 4203252
    |          21 |
    |          12 |
    +-------------+

好吧,看这情景,我怎么回答都算不上错,因为哪有正确答案啊。

可惜把 Borland C++ Builder 和 Open Watcom 编译器都删了,不然如果看到 5 种不同的结果岂不是更欢乐~ 唉,难怪 C++ 木有 ABI。

另外,被问到虚函数表在对象的什么部位时我发烧似的答道“在尾部”,囧啊,谁教我的。(不过另一方面,可以诡辩一下的是, C++ 2003 标准甚至没有规定运行期多态一定要以虚函数表的形式实现,只是目前的编译器不约而同的采用了虚函数表而已,所以,嗯...)

Update:

Open Watcom 编译出来的结果是这样的:

a:
  +-------------+
  |          42 |
  |          24 |
  |     4240484 | -> 4198782
  +-------------+
b:
  +-------------+
  |     4240544 | -> 0
  |           8 |
  |          42 |
  |          24 |
  |     4240556 | -> 4198782
  +-------------+
c:
  +-------------+
  |     4240488 | -> 0
  |     4240500 | -> 4198952
  |          12 |
  |          21 |
  |          12 |
  |     4240508 | -> 4198866
  +-------------+
d:
  +-------------+
  |     4240520 | -> 0
  |     4240532 | -> 4198952
  |     4240512 | -> 8
  |          16 |
  |          21 |
  |          12 |
  |     4240540 | -> 4199045
  +-------------+

果然又不同——不过,惊喜啊,它还真把虚函数表放在了尾部。

2. 脑袋短路

还是网易面试,几个算法题都发挥得不够满意,甚至包括生成随机数的题,回头想想也有改进的空间。

嗯...不知道试题有没有涉及到某种需要保密的东西,所以暂时先不谈好了……

3. 春天来了

img_0059.jpg
ps. 实践表明,GCC 编译器会给类的成员函数添一个隐含的 this 参数,通常会自动赋值,然而用奇淫手段弄到了函数地址时也可以手动传对象地址去,就像在 python 中对待 self 那样;然而其他的 C++ 编译器似乎都没这么干,至少你把 void A::f() 当作 void f(A*) 来玩是不行的,可惜了,不然会挺好玩的。

流水帐+意识流

灰机

第二次坐灰机,在这个位置坐着,玻璃如果破了我就会被吸出去,然后在这个引擎里面被卷死掉,引擎内圈会变成红色,我的血和肉块会洒到地面,然而由于是晚上所以地面上不见得会有人发现;另一方面,这种死法肯定比摔下去要好,因为可以免去了很长一段等死的阶段——只要我别太怕死,双手紧抱住引擎的边缘,以至于一点一点的被吸进去。

 

某次 GR 上看到有人分享宅男必备的几个玩具,其中之一便是 Arduino,手痒,便入手了一个(同时还买了个遥控车,打算拆了改用 Arduino 控制,没到手);然而此刻我正在学校,手头上不但没有发光二极管、蜂鸣器、马达、高压包、电烙铁这些老朋友,甚至连导线都没有,于是唯一能用的输出是 13 号口自带的一个发光二极管。

一个发光二极管,那能玩的就只有摩尔斯电码了:

#define UNIT_TIME 250
#define OUTPUT_PIN 13

static char const mCode[][8]= {
    "-----", // O
    ".----", // 1 
    "..---", // 2
    "...--", // 3
    "....-", // 4
    ".....", // 5
    "-....", // 6
    "--...", // 7
    "---..", // 8
    "----.", // 9

    ".-",    // A
    "-...",  // B
    "-.-.",  // C
    "-..",   // D
    ".",     // E
    "..-.",  // F
    "--.",   // G
    "....",  // H
    "..",    // I
    ".---",  // J
    "-.-",   // K
    ".-..",  // L
    "--",    // M
    "-.",    // N
    "---",   // O
    ".--.",  // P
    "--.-",  // Q
    ".-.",   // R
    "...",   // S
    "-",     // T
    "..-",   // U
    "...-",  // V
    ".--",   // W
    "-..-",  // X
    "-.--",  // Y
    "--.."   // Z
};

void light(int time) {
    digitalWrite(OUTPUT_PIN, HIGH);
    delay(time);
    digitalWrite(OUTPUT_PIN, LOW);
}

void alpha(char c) {
    char const *code=0;
    if(c>='0' && c<='9') {
        code=mCode[c-'0'];
    } else if(c>='a' && c<='z') {
        code=mCode[c-'a'+10];
    } else if(c>='A' && c<='Z') {
        code=mCode[c-'A'+10];
    } else {
        delay(4*UNIT_TIME); // 应该停顿 7 个单位,不过每个单词结束后都已经停顿了 3 个单位时间
        return;
    } 
    for(;*code;++code) {
        switch(*code) {
        case '.':
            light(UNIT_TIME);
            break;
        case '-':
            light(3*UNIT_TIME);
            break;
        }
        delay(UNIT_TIME);
    }
    delay(2*UNIT_TIME);
}

void say(char const* sentence) {
    for(char const* p=sentence; *p; ++p) {
        alpha(*p);
    }
}

void setup() {
    pinMode(OUTPUT_PIN, OUTPUT);
}

void loop() {
    say("I Feel So Alone   ");
}

wikipedia 说同一个单词不同字母间有三个单位的间隔,但我发现这很不好认;接着我就想如果再多买一块板子,接上光敏电阻,一个亮,一个读,岂不是很好玩;接着又想,如果真有两块板子,那我何必用摩尔斯电码呢,用灭灯代表 0,亮灯代表 1,直接传输就好,爱折腾的话想办法用数组保存一个已经预先设定好的霍夫曼编码似乎更可以装B了,接着我又想,那我是不是要想办法让它知道什么时候是对方传输完了,什么时候是在传输一大堆的 0 呢,要不在每组数据末尾加上一个 END,开头加上 BEGIN?可是那我真要传输 BEGIN 这个单词了又怎么办?用 \ 标记?对呀,然后 \ 就表示为 \\,太完美了——可是那突然掉电怎么办,要么每隔一秒种说一次“还没完!”?…………。

Update: 我傻,用校验码啊

 

另外,开始看了看 Haskell,因为它的自我介绍很有意思,大意是 “即使你学了 Haskell 什么实际用途都没有,至少也可以提高你的编程能力”,好吧,这个我相信。

另外就我目前的认识来看 Haskell 推理形式的函数定义方法和 C++ template 元编程简直太像了 @_@ 说 C++ 恐怖果然是有道理的……