简化类方法的形参多态

最近在看《重构与模式》一书的修订版,想借用思路简化一下我手头的代码,不觉间发现了个简化类方法形参多态重载的方法,也许是书里已经有我还没看到,或也许还是个新的。

问题是这样的,我建立了一个有向图的数据结构,其中节点、弧和图的简化数据结构如下:

class Node {       // 节点
public:
  vertex_descriptor id;     // 节点全局唯一的id
  std::string       name;   // 节点名称
  boost::shared_ptr<Graph> pg;   // 图指针
};
class Edge {       // 弧
public: 
  edge_descriptor id;       // 弧全局唯一的id
  std::string     name;     // 弧的名字(不唯一)
  vertex_descriptor src;    // 起点节点id
  vertex_descriptor tar;    // 终点节点id
  boost::shared_ptr<Graph> pg;   // 图指针
};
class Graph {      // 有向图
public:
  std::map<vertex_descriptor, boost::shared_ptr<Node> > nodes;     // 节点表
  std::map<std::string,vertex_descriptor>               node_map;  // 节点名称和id转换表
  std::map<edge_descriptor, boost::shared_ptr<Edge> >   edges;     // 弧表
};

一个节点,可以由三种方式唯一的标定:id、节点名称和其节点指针。在Graph里面,有很多关于节点的操作,比如是否存在(exist)一个节点或者边,获得一个节点的所有入边、入节点、出边和出节点,等等。只是用id表示的方法定义如下:

class Graph{
.....
  bool exist(vertex_descriptor);  // 是否存在一个节点
  bool exist(vertex_descriptor, vertex_descriptor); // 是否存在从一个节点到另一个节点的弧
  std::list<boost::shared_ptr<Node> > get_in_nodes(vertex_descriptor); // 获得所有入节点
  std::list<boost::shared_ptr<Edge> > get_in_edges(vertex_descriptor); // 获得所有入边
  std::list<boost::shared_ptr<Node> > get_out_nodes(vertex_descriptor); // 获得所有入节点
  std::list<boost::shared_ptr<Edge> > get_out_edges(vertex_descriptor); // 获得所有入边
};

这些方法的每一vertex_descriptor类型的参数,都可以换为std::string或者boost::shared_ptr<Node>来唯一标示一个节点。如果要完成每一种参数类型的多态,一个形参的函数有三种实现,两个参数的函数则有9种,显然会出现大量重复的代码。

解决这个问题需要用到STL和宏。用STL定义一个函数的泛型,然后将所有的泛型都指向同一个特例(使用vertex_descriptor的方法实现)。利用宏减少代码量。先讨论STL的用法。比如说bool exist(vertex_descriptor);方法,我们可以定义:

template
bool exist(T d) {
  return exist(to_id(d));
}

这里的T即可是id, string和指针中的任何一种。无论是哪一种,可以通过to_id()方法将其换成id,然后调用使用vertex_descriptor的方法处理。关于exist并包含to_id方法的完整代码如下:

vertex_descriptor to_id(std::string name) const {
  if(node_map.count(name))
    return node_map.find(name)->second;
  else
    return NULL;   // vertex_descriptor的类型实为void*
}
vertex_descriptor to_id(boost::shared_ptr<Node> pnode) const {
  if(pnode)  return pnode->id;
  else       return NULL;
}

template<typename T>
bool exist(T d) {
  return exist(to_id(d));
}
bool exist(vertex_descriptor);

这看起来比写3遍相同的代码省不到哪里,但是to_id方法可以被所有其他的方法共用,STL的写法也不会因为形参数量变多而变的更复杂。

更进一步,我们可以定义以下的两个宏来简化STL的定义:

#define FUNC_HELPER1(rvalue, fname)  \
template<typename T>   \
rvalue fname(T d) {                  \
  return fname(to_id(d));            \
}

#define FUNC_HELPER2(rvalue, fname)   \
template<typename T1, typename T2>    \
rvalue fname(T1 d1, T2 d2) {          \
  return fname(to_id(d1), to_id(d2)); \
}

为一个参数和两个参数的方法定义了两个宏辅助定义。利用该辅助定义,能够实现任意形参类型多态的方法定义如下:

class Graph{
.....
  FUNC_HELPER1(bool, exist);
  bool exist(vertex_descriptor);  // 是否存在一个节点
  FUNC_HELPER2(bool, exist);
  bool exist(vertex_descriptor, vertex_descriptor); // 是否存在从一个节点到另一个节点的弧
  FUNC_HELPER1(std::list<boost::shared_ptr<Node> >, get_in_nodes);
  std::list<boost::shared_ptr<Node> > get_in_nodes(vertex_descriptor); // 获得所有入节点
  FUNC_HELPER1(std::list<boost::shared_ptr<Edge> >, get_in_edges);
  std::list<boost::shared_ptr<Edge> > get_in_edges(vertex_descriptor); // 获得所有入边
  FUNC_HELPER1(std::list<boost::shared_ptr<Node> >, get_out_nodes);
  std::list<boost::shared_ptr<Node> > get_out_nodes(vertex_descriptor); // 获得所有入节点
  FUNC_HELPER1(std::list<boost::shared_ptr<Edge> >, get_out_edges);
  std::list<boost::shared_ptr<Edge> > get_out_edges(vertex_descriptor); // 获得所有入边
};

所有的方法为了实现形参多态实际只需要在头文件中添加一行代码就完成了。(当然to_id和宏是需要添加的,但只需要写一次)。

Advertisements

外国驾照换中国机动车驾驶证流程(北京)

前几天已经成功拿到了北京市交通管理局颁发的机动车驾驶证。虽然只用了一周,但整个流程不是很清楚,各个网站的叙述也有差别。所以将我的理解和流程记录如下,以助其他有相同需求的同学回国换照。
政策规定:
根据北京市交通管理局关于持境外机动车驾驶证申请机动车驾驶证的相关规定(全国一致)
1.持境外机动车驾驶证可申换同等中国机动车驾驶证。
2.其申请地点为户口/身份证所在地的交通管理所,或北京市交通管理局总所涉外大厅(持外国国籍者)。
3.所有申请者需要加考考试科目一。申请准驾车型为大型客车、牵引车、中型客车、大型货车机动车驾驶证的,还应当加考考试科目三。持比利时机动车驾驶证和外国驻华外交人员可免试直接换取。
4.科目一费用50元,考试通过后换证费用10元。
换证申请所需文件:
1. 申请者1寸白底彩色登记照4张(身体证明1张,交管所3张)(注:按规定近视者需戴眼镜照相,但执行不严)
2. 《机动车驾驶证申请表》(可网上下载或去交管所时直接填写)
3.北京市卫生局确定的县级以上医疗机构或军队、武装警察部队确定的团级以上医疗机构出具的有关身体条件的医疗证明;持境外机动车驾驶证的外国驻华使馆、领馆人员及国际组织驻华代表机构人员,不需要提交身体条件的医疗证明。北京市可开出证明的医院可在这里查到:http://www.bjjtgl.gov.cn/fwzn/yymc.htm
4.申请人的身份证明(身份证,外国国籍者持护照加翻译)
5.申请人所持境外机动车驾驶证和中文翻译文本。
办理流程:
1. 照相(必须,医院和交管所可能没有照相机)
2. 去指定医院开具身体证明。(我去的海淀医院,不挂号,直接去体检中心驾驶员体检室,交钱体检即可。交管所和驾校有时也可办理。)
3. 翻译外文证件(国外驾照、护照),可自己翻译,需要在翻译件上签署翻译人姓名和日期,翻译公司加盖公章。
4. 预约对应交管(也可直接去,海淀和门头沟在京海交管所,一个坐完地铁还要坐公交80分钟的地方)
5. 去交管所递交所有文件和预约理论一考试时间(北京基本没有等待时间,第二天考试都行)。
6. 复习理论考试。
7. 在指定时间去考场考试。
8. 考试完毕当场拿到成绩后,返回交管所递交成绩。如果通过,当天或5天内领取驾照。(没过则预约补考,只有一次补考机会)
附加说明:
1.按规定近视者照片应戴眼镜,但交管所人员显然放松执行
2.交管所一般旁边就是驾校,所以一般可以在交管所直接体检
3.交管所申请时会要求输入食指指纹,请认真输入(设备不好,记住自己输入时的姿势)
4.考试场往往是循环考试,所以去早了去晚了差不多,都是排队然后找空上机考试。
5.考试开始时需要按指纹进入考试。三次指纹失败后重新登录再重输指纹。
6.如果多次指纹都不对,不要慌张,让监考官绕过指纹直接开启考试(我就是这样,搞不明白是我指纹错了还是指纹设备实在太烂,我觉得是后者)。
7.翻译时要签名和日期。同时需要解释驾驶证上的准驾类型

中英交规比较

今年回国顺带用刚拿到不久的英国驾照换中国机动车驾驶证。按规定我需要加考理论一,无可避免的我把中国交规考试的题库给做了好几遍。过程中有时会笑到喷饭,不比不知道,这中英交规的差别还是不小的。因为我拿的是英国B照(中国C1),这里只讨论小型手动机动车的交规。

一、明显区别:
1. 中国是靠右行驶,而英国是靠左行驶。不多说,大家都知道。
2. 中国允许在红灯时右转车适量通行,而英国不允许红灯时左转车通行(即所有车停车)。
评论:左右行驶问题很正常,不过这个红灯右转通行,在我看来简直是行人杀手。由于右转通行,加上中国的红绿灯是全时半灯开启(即没有四向红灯的行人专用时间),任何时候想过马路都需要考虑3个方向的来车(绿灯右转,反向绿灯左转,侧向红灯右转),看漏一个车还真是容易。加上中国现在的车可不让人,不出事故才见鬼呢。相比英国,人行道绿灯意味着该人行道上的所有机动车方向都是红灯,行人被撞一定是机动车闯红灯。相比安全很多。

二、速度区别:
1. 高速公路最高时速中国120km/h 英国70mph(112km/h),中国相对速度更快
2. 城市公路(有中心线,有路灯)中国为50km/h和70km/h 英国30mph(48km/h),仍然中国相对更快
3. 高速公路匝道速度 中国为60km/h 英国没有规定,但一般至少加速到40mph(64km/h)才能正常进入高速公路,速度差不多,英国相对快
4. 高速公路最低速度 中国为60km/h 英国没有规定,但低于40mph基本会被拦下
5. 转弯速度 中国规定不可高于30km/h 英国没有规定
6. 当高速公路能见度低于50m时,中国规定用30km/h以下速度驶离高速,英国没有规定,应该是低速驶离
评论:各种正常行驶的最高限速和最低限速都算合理。不过这个能见度底下时的30km/h(20mph),也不知道是谁拍拍脑袋就给定了。在30km/h下的紧急制动距离是12m,而80km/h的紧急制动距离是53m,也就是说速度在70~80km/h速度内,50m是可以停下来的。如果一车真的降到了 30km/h而后面的跟车没降,仍然开在120km/h会发生什么?120km/h的制动距离是96m,当看到前车时,距离只有50m,即使后车紧急制动,仍然需要80多米才能降到30km/h,显然就撞上了。前车在多少速度时,后车开120km/h才能在50m内降到同速呢?大概85km/h。所以,即使发生了大雾,开在70~80km/h才是安全的,既能在50米内停下来,后车也能及时降速不撞到你(假设后车还是降了些速度)。开在30km/h这仅比自行车快不了多少的速度上,不被撞才见鬼了。

三、间距要求:
中国规定高速上高于100km/h时间距至少50m,高于100km/h时间距要100m
英国没有硬性要求,不过建议车距要大到能够紧急急停。在最高70mph的速度下制动距离96m
评论:两国规定不一样,但效果差不多,真正在高速上谁也不会真的间距100m,基本都不遵守

四、处罚(这个差别最大了):
1.两国都是积分制,皆为12分,记分周期中国12个月,英国3年
2.酒后驾驶 中国记12分暂扣驾照6个月学习重考交规加最多2000元罚金;英国最高吊销驾照,入狱3个月或2500英镑罚款
3.醉酒驾驶 中国记12分吊销驾照5年加最多2000元罚金;英国吊销驾照至少1年,入狱6个月或5000英镑罚金
4.酒后撞人致死 中国除醉酒驾驶之外加入狱最多12年;英国除醉酒驾驶外加入狱10年,无最高上限罚金加至少2年禁驾
5.无照驾驶 中国记12分罚金2000 英国3~6分加最高5000英镑罚款
6.无保险 中国扣留车辆,保险2倍金额罚款 英国记3分,2500英镑罚款
7.超速50% 中国记6分加罚款 英国记6分加2500英镑罚款
8.低速超速,闯红灯,其他违规 中国记3分加罚款 英国记3分加1000英镑罚款
评论:看起来差不多,但其实差别很大。英国的分值钱多了,12分管3年(开始两年只有6分),三年内12分没有是重新加长考试所有科目;相比中国仅仅是1 年内12分,且暂扣驾照只加长考理论1。另外,英国路考的平均通过率低于50%,一般需要两次才能通过(俺是2次过的,葛同学4次还没过,还有朋友考了1 年了),中国我就不说什么了。另外,英国的罚金比中国的重多了,随便什么被罚都是1000英镑以上,中国最多也才2000元,1/5而已。我觉得,这也是为什么中国交规都不遵守,因为即使吊销了也只不过重考个理论,实在不算什么。拘留也才15天,英国喝个酒可能要入狱1年,呵呵。

五、其他差别:
1.中国理论题目里面居然有教如何跳车和在撞车时如何防止被方向盘卡住。看来中国开车也是hard模式的,逃生很重要,主侧气囊都是靠不住的,跳车是正道!?
2.中国交规说交叉路口转弯车,右转弯应该先让对方向左转弯因为他们更难转。可是对方左转弯要让本方直行,如果右转弯和直行在一起到底让不让?而且,这个至少和英国的规定就不一样,英国的右转弯(相当于左转弯)永远优先级最低,左转弯其次,直行最高。
3.中国交规里面是个什么都要鸣个笛,还有个牌子表示必须鸣笛。英国只有一个地方提到了鸣笛,就是对方在倒车看不见你,你需要鸣笛好让对方停止倒车。不仅如此,交规还规定晚上11点到早上7点在居民区不准鸣笛。难怪中国马路上笛声一片。

综上所述,中国交规相比英国规定更严更细,但是惩罚明显很松。在交规的定义上,中国交规分布在不停更新的各个法律文本和政府规定中,而英国将所有的内容都集中在high way code里统一定义并提供公众查阅。英国交规虽然定义较粗,但容易执行和查阅。