Published on
2010年01月30日 in
备忘.
Tags: C, trick.
写小东西需要用到哈希表这样的数据结构时候才后悔没用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;
}
终究不如模板来的自然,呵呵~不过也不错了。
前些天写html生成器的时候用到了erb,在生成html的时候是这么一句:
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汇编,这书可能就不大合适了。汇编语言还是隐藏了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在参数传递时压栈的顺序貌似就是相反的。
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的路径,要不然会乱套。也就是这样:
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可以这样:
在变量名前面加*的含义貌似就是将它里面的元素看作一个整体:
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
Comment