Tuesday, April 1, 2008

序列化serialization,元编程metaprogramming,反射reflexivity,quine,语法糖衣syntactic sugar

很好很强大,一上来就遇到这么一长串题目.
今晚偶尔无聊看了一下这几个概念,其实每个概念都可以引申出一长串东西了...
不过为了做个备忘,还是稍微写写吧.不保证我理解和语言表达的正确性.最终解释均以wikipedia为准.

1.序列化,serialization
序列化可以保持对象的持久性(简单来说就是把你程序运行时对象在内存的映像保存起来,写到非易失性存储介质上).
对于普通的一个数据结构,比如
class A
{
    int a;
    char b;
}
这样的东东显然很容易做到,比如我只要简单地把一个4B的int和1B的char保存下来就好.
如果有一堆这样的对象,比如十个A对象,也许我可以定义我保存下来这个文件,前4个字节保存的是后面含有的A对象数,
然后就可以遍历读出来了.

但是,如果对于一个链表呢?
class Node
{
    int value;
    Node * next;
}
我们在创建此类对象时一般是使用动态内存分配的.那么就要考虑到:对于每个run time,显然分配到的内存地址是不大可能一样的.
因为这涉及到了内存的relocate.
那么我们应该怎麽保存这种对象数据呢?这就涉及到了序列化与逆序列化(serialization,deserialization)

More specifically,
从一个对象/指针/地址的引用转换到在存储器上的表示,可以称为"pointer unswizzling"(我也不知中文怎麽喊),也即是serialization的过程.
而反过来,从一个序列化的文件转换为一个对象可以称为"pointer swizzling",也即是deserialization的过程.

完成序列化的方法是多种多样的,还是以链表为例子.
我们可以遍历一个链表,然后给每个节点一个内部标号,最后存储时描述的数据结构就可以如下:
class _Node
{
    int value;
    int _id;
    int _next_id;
}
因此每个对象之间的相对关系可以用偏移量计算出来,因此deserialization也是一样的原理了.当然这里还有很多情况没有分析到.
不过大概的一种实现方法就就是如此.

不同语言有不同的序列化方法.我所接触过的,有
1)Java的serialization mechanism,通过声明某对象为serializable
2)MFC的serialization
3)C++的boost库的serialization实现
其余语言略.

2.元编程(meta programming)
元编程简单来说就是可编程的编程...或者理解成自动生成代码,动态改变指令也行.
因此引申出两个概念,一个是符合这种定义/有这种功能的语言--元语言,一个是该语言能生成的语言--目标语言.如果这种语言的目标语言就是它本身,那么我们称它是自反的(反射的,reflective).

比如yacc,lex等,就是一种生成语言的语言...只不过是在源代码级的.
而我们常见的"宏",通过在编译前的预处理帮助我们生成代码,这种是宏拓展,也是在源码级的.
最强大的莫过于运行时动态修改了.现代程序设计的模型(图灵机)是把指令与数据同等对待的,
那么既然我们能动态修改数据,为什么不能修改指令呢?当然要实施相应的安全机制拉.....

比如说最近我想写这么一个脚本,不过我是脚本菜鸟 -v-
我想定义一系列的shell的环境变量,分别是for i in seq `1 8`;ip$i=192.168.1.$i;
问题是,ip[1-8]这些变量现在还没定义阿...因此后面也不能进行export.于是我只好折中地先写一个脚本生成要执行的语句,
然后再执行生成的脚本,分两步执行.殊不知这也算是一种元编程(在wikipedia上是这么说的...)
我想,如果能动态生成变量就好了.其实也许是可以的,只不过我不会而已.....

另一个典型的例子是java的反射机制,这又要涉及到了所谓的first-class datatype.关于这个就先不说了.
在经典的C/C++等语言中,基本的数据类型/对象有int,char等这些,但是在java中,连类,方法本身也是一种对象!
因此对于类,方法,我们可以保存一些元信息,来做到一些强悍的事情.

比如我们可以动态地执行一个方法,通过接收用户的输入,然后在类里面查找相应的方法,创建一个这个方法的引用,
然后调用之.这在以往的C/C++中根本不可想象.

C++貌似也想加入一点这个特性,比如RTTI也算是一个半成品.另外template也多少有点这么个意思.
只不过template现在引出的是generic programming,范型编程,此处不表...

关于元编程,我理解的就这么多,还要更都的可以去学习一下lisp等...可以参见这位大牛...
http://pluskid.lifegoo.com/?p=46#more-46

最后要小小地说一下"quine",在programming领域中,它指的是"能够打印自己源代码的程序".
往往被hacker们用来show自己对某种语言的熟练与掌握程度.
Quine 以哲学家 Willard van Orman Quine (1908-2000) 而命名.因为他提出了" indirect self-reference"这个概念.
与它相关的有这么一个悖论:"This sentence is false."
那么,究竟那句sentence是对还是错呢 ^_^ 说它对时,也就承认了它是错的;说它错时,也就承认了它是对的......
一个最简单的C-quine如下,很强大:
main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);}

更多的请继续参看这位童鞋的wiki:
http://pluskid.lifegoo.com/wiki/Quine.html

3.语法糖衣syntactic sugar
这个比较好理解.我们知道一种语言有固定的语法结构与关键字.
程序设计的三种基本结构为顺序,选择,循环.
因此完成了这些基本功能的语言的语法与关键字我们可以看作一个集合,在这个集合里面我们可以干出程序允许的任何事.
但是如果我们添加一些关键字后,也许可以增强程序的可读性,或者降低编程的复杂度,然而我们不能做出比原来更多的事!

举一个例子,比如在C++中,我们完全可以使用for来完成所有的循环,而while可以改写成for.
另外由if-else结构也能完成所有的条件语句,switch, :? 这些都可以转化成if-else的表示.
因此很直观地,我们可以定义这种语言的语法的"核心"(core syntax).
语法糖衣是好的还是不好的?也许对于我们这些普通人他们是好的,习惯了写for loop的我在写shell程序时觉得写一个for i in ...
觉得很脑残,幸而有for((;;)) 这个糖衣帮我解决了问题.
然而大牛们一致认为这是没有必要的,因为单靠最基本的语法就能解决问题.
这又牵涉到了数学问题,学过scheme等函数式语言的都知道,所有语句其实都是由表达式递归定义而成,包括循环,选择.
因此大牛们写程序往往可以用几个括号,四则运算,数字就搞定了....
Linux内核维护者也坚持只需要用C来完成OO以及所有功能,而不需要C++等.
更直观地说,比如我们可以这种定义正整数:
1)最小的数是1
2)第i大的数是比它前一位的数加1
(可能写得有点不准确,不过递归定义的思想基本就如此)

然后还有两个不那么流行的术语:
语法盐 syntactic salt
被用于增强程序健壮性或代码编写规范的语法,它们本身并不能简化程序的写法或增强语言的功能.
比如一些程序语言中提倡if与endif要配对,而不是简单地用{}括起来.

语法糖精 Syntactic saccharin
这个比较无聊,指那些并不能使程序编写简单的无用的特性.

---分割线来了---

恩,好了,大概就联想这么多...
愚人节就让我geek一回...

No comments: