Tag Archive for 'trick'

C的泛型库khash

写小东西需要用到哈希表这样的数据结构时候才后悔没用C++,最起码还有stl能用来着。虽说C也能搞泛型,不过宏实现的泛型…真的好恐怖。

tinyrb用了khash做哈希表,据作者说已经是一个稳定的实现了。

khash.h的内容:
http://attractivechaos.awardspace.com/khash.h.html

大体可以这么用:

#include "stdio.h"
#include "khash.h"
KHASH_MAP_INIT_STR(str, int) //以“str”这名字初始化一个类型的map,键类型为字符串,值类型为int
int main() {
    int ret, is_missing;
    khiter_t k; //khash的索引器,好像就是个int
    khash_t(str) *h = kh_init(str); //str只是个名字,初始化
    k = kh_put(str, h, "test", &ret); //“test”即键,ret判定操作是否成功,返回k为索引器
 
    if (!ret) kh_del(str, h, k); //如果h中已经存在了这个键,就删除之
    kh_value(h, k) = 10; //设置键“test”对应的值(10)
 
    printf("%d\n", kh_val(h,k)); //kh_val(h,k)即10
 
    k = kh_get(str, h, "test"); //获得“test”对应的索引器k
    printf("%d\n", kh_val(h,k)); //得10
 
    kh_destroy(str, h);
    return 0;
}

终究不如模板来的自然,呵呵~不过也不错了。

fdoc

呃,一直没搞明白txt2tags这gui该怎么玩…

就用ruby写了这东西 http://code.google.com/p/fdoc/
可以把这样的文本
http://code.google.com/p/lyah-cn/source/browse/trunk/txt/2-ready-go.txt
转换成这样的html
http://fleurer-lee.com/lyah/ready-begin.htm

bug很多啦~不过貌似暂时够用?

愿春哥原谅我们复造轮子者之罪孽,耶门~

eval, class_eval, instance_eval和binding

前些天写html生成器的时候用到了erb,在生成html的时候是这么一句:

html=tpl.result(binding)

binding这个变量(Kernel的一个方法 T_T)有点古怪,就搜了下。它表示了ruby的当前作用域,没有任何对外可见的成员函数,唯一的用途就是传递给eval作第二个参数。因而可以这样:

def test_binding
    magic='brother Chun is PURE MAN'
    return binding
end
eval "puts magic", test_binding

这样就穿越了一个作用域。

有时可以见到这样的构造函数:

a=Baby.new {
    name "Makr"
    father "Mike"
    age 0.2
}
a.cry

好处就是好看。实现起来其实也很容易,用instance_eval:

class Baby
    def initialize(&blc)
        instance_eval(&blc) #here
    end
 
    def name(str=nil)
        @name=str if str
        @name
    end
    def age(num=nil)
        @age=num if num
        @age
    end
    def father(str=nil)
        @father=str if str
        @father
    end
    def cry
        puts "#{name} is only #{age.to_s} year old, he wanna milk! Brother Chun is PURE MAN!"
    end
end

有重复代码?用class_eval缩短之,有点像宏了:

class Baby
    def initialize(&blc)
        instance_eval(&blc)
    end
 
    def Baby.my_attr(*names)
        names.each{|n|
            class_eval %{
                def #{n}(x=nil)
                    @#{n}=x if x
                    @#{n}
                end
            }
        }
    end
 
    my_attr :name, :father, :age
 
    def cry
        puts "#{name} is only #{age.to_s} year old, he wanna milk! Brother Chun is PURE MAN!"
    end
end
 
a=Baby.new {
    name "Makr"
    father "Mike"
    age 0.2
}
a.cry

这里class_eval穿越到了类的作用域,实现了动态添加函数。instance_eval也是,穿越到了实例的作用域,实现修改其内部数据。明白了它们的穿越关系,我们可以实现自己的class_eval和instance_eval——从合适的地方搞到binding就行了。

class Baby
    def my_instance_eval(code)
        eval code, binding
    end
    def Baby.my_class_eval(code='')
        eval code, binding
    end
end

就这么简单。调用的时候就像这样:

class Baby
    def initialize(code)
        my_instance_eval(code)
    end
    my_attr :name, :father, :age
end
a=Baby.new %{
    name "Test"
    father "Orz"
    age 0.2
}

刚才省略了一点,那就是class_eval和instance_eval可以接受block代替字符串。搜了下,貌似没找到eval接受block的方法,所以这顶多算是只能eval字符串的山寨class_eval。

update: 想起来ruby中lambda和proc在作用域上的小区别,也就是binding的不同了。proc直接使用原先的binding,lambda继承原先作用域创建一个新的binding。

mips汇编入门

据说若要深入学习MIPS开发的话,《MIPS处理器设计透视》这书是必不可少的。不过若只是学习MIPS汇编,这书可能就不大合适了。汇编语言还是隐藏了CPU的很多细节,而这本书里讲的貌似就是这部分,在对汇编有所了解之后再来阅读可能要更好。

学习函数式语言的时候总是满足于看书,理解下语法语义即可,真正写的代码则少的可怜,不过确实“改变了编程的看法”,目的也算达到了。汇编就不行了,看书不够,一定得动手。所以学MIPS需要一个模拟器。pcspim应该是比较标准的了,不过感觉Mars可能要更好用(准确地说,Mars非常非常好用)。

MIPS是以优雅著称,据说即使是其竞争对手也如此认为。RISC么,32个寄存器,指令长度都一样。其中的指令大约这么三种形式:

j 1000
li $1, 10
add $1, $2,$3

差异就是各个参数的长度不同。如add指令的三个参数都只有两个位宽(0~255),每个参数表示一个寄存器。如果把指令看作函数,那参数就可以看作是有类型的。而MIPS的汇编器是很强大的(听说可以进行窥孔优化),像add $t0, $0, 10这样的指令会被汇编器翻译成addi $t0,$0,10。汇编器处理前后指令的对比可以在Mars中显示出来。

记几个helloworld吧,

求3的阶乘:

li $t0, 0
li $t1, 1
if_1:
add $t0, $t0, 1
mul $t1, $t1, $t0
bne $t0, 3, if_1

在Mars下可以看到寄存器的变化,最后$t1寄存器的值是6。

mips汇编的分支(branch)指令分b系(bne,beq,bgt等等)和j系(j, jr等),差别就是b系指令的跳转都是有条件的,而且地址在参数中指明,而j系的跳转都是无条件的。j系指令的地址长度更长,寻址范围要更大,所以远程跳转都是j。

输出Helloworld:

.text
.globl main
 
main:
li $v0, 4                     # just the print syscall in SPIM
la $a0, str
syscall
 
.data
str:
.asciiz "hello world"

这应该算个比较完整的汇编程序了。程序的可执行代码都是在.text段,数据在.data段。.globl指明程序的入口地址,那个:str指代的就是这段字符串的地址。字符串么,就是数组。数组不就是指针么。

其中这个syscall会与操作系统的不同而有差异。系统调用的号码由$v0指明,参数在$a系的寄存器中传递,返回值放回到$v0。这里调用的是spim实现的4号系统调用,即print string。

定义一个函数f_add,它可以将两个数相加:

.text
.globl main
 
f_add:
add $v0, $a0, $a1
jr $ra
 
main:
li $a0, 1
li $a1, 2
jal f_add
 
add $t0, $v0, $0
 
li $v0, 1
add $a0, $0, $t0
syscall

在使用jal指令的时候,它会把发生跳转的地址记录在$ra寄存器中。这样函数在结尾的时候就可以用jr返回原先的位置了。

使用寄存器传递参数的好处貌似就是约定了函数的调用规范,兼容性要更好。例如x86下的BASIC和C在参数传递时压栈的顺序貌似就是相反的。

note on ruby

Ruby和python这两门语言貌似不应该有可比性。C\C++出身的程序员们都喜欢Python带来的生产力,而ruby被发明则是为了取代perl—-做为脚本语言。脚本语言么,效率不必太高,要容易书写,容易读貌似倒不是很重要…瞧瞧bash和perl吧。而Ruby强大的表达能力就弥补了可读性的不足,再加上已经足够多的第三方库(gems),可以算是一门理想的语言了。可惜就这门语言而言(不谈rails),在国内(国外github上ruby可是第一语言啊)一直没能很成气候呢(CpyUG这个社区太强大了,ruby在国内就没有对应物)。貌似大家都在搞rails的敏捷开发,没人搞ruby?

呃,跑题了。这两天用ruby写点小玩具,记几个小tricks就是了。玩具而已。玩具而已。

判断类型,可以这样obj.is_a? Array 。如果不喜欢问号,可以Array === obj,貌似要好看点?注意的地方就是类型对象要放在前面。

单元测试是好东西,一般源码会位于src文件夹,而测试文件会放在test文件夹。测试文件需要require源码的文件,这就要修改require的路径,要不然会乱套。也就是这样:

$:.unshift "../src"

update:$:其实是个全局数组。require的路径皆来自于此。

正则表达式,模式匹配。好吧,玩haskell后遗症。

test=
    case content
    when /^abc(.*)/
         $1
    when /^abcd(.*)/
         $1
     end

ruby里所有的语句都是表达式。正则表达式匹配的时候会修改几个全局变量,
$& 整个匹配
$1 第一个匹配
$2 第二个匹配
$` 位于匹配前的字符串
$’ 位于匹配后的字符串

很明显,都是继承自perl。Perl那堆带美元符号的变量可是饱受扣病啊,在《the ruby way》这书里作者貌似刻意地回避这种全局变量的使用。不过自己看着办就好了。

谈到模式匹配,haskell可以用(x:xs)这样的模式切开一个list得到首元素。Ruby可以这样:

x, *xs=[1,2,3,4]

在变量名前面加*的含义貌似就是将它里面的元素看作一个整体:

def add(a,b)
a+b
end
paras=[1,2]
add(*paras)

在处理可变长度参数时候也是如此:

def add(*paras)
paras.inject{|acc,i| acc+=i}
end

呃,inject就是python的reduce,haskell的fold。

block在ruby中应该是无所不在了,也算是函数式吧,不过搞的比较自然,谁也不会想到那儿去。(话说,貌似有不少同学都是在python的那个lambda关键字才了解到函数式编程的…ruby倒也有个lambda,不过只是个Proc类的实例,貌似没谁用 – -!)。在带block的函数递归的时候一定要记得传递这个block。

def travel(arr, &block)
  x,*xs=arr
  block.call(x)
  travel(xs, &block)
end