编程便笺:代码的图形(一)

      编程时,程序员心中会有图形,而有些代码本身就是图形,不但可读而且可看。

#include "stdafx.h"
#include <deque>
#include <map>
#include <string>

using namespace std;

//图形由点组成
class TNode
{
public:
    wchar_t * name;
    //构造函数
    TNode(wchar_t * _name):name(_name){}
    virtual const wchar_t * toString(){return name;}
};

class TEmp;
//组织结构
class TOrg : public TNode
{
public:
    TEmp* manager; //负责人
    TOrg* parent;  //直接上级组织
    TOrg(wchar_t * _name): TNode(_name),manager(NULL),parent(NULL){}
};
//职员
class TEmp : public TNode
{
public:
    TOrg * org; //所属组织
    TEmp(wchar_t * _name) : TNode(_name),org(NULL){}
};

//定义空节点,效果类似于NULL,从下面的代码看出,是非常需要的。
TNode nil(NULL);

/*以下是一些辅助函数,这些辅助函数用于以下的宏,使用辅助函数可以减少
  宏的付作用而引起意外的结果。
*/
//指定节点的上下级关系
TNode & operator << (TNode & _Left , TNode & _Right)
{
    if(&_Left == &nil || & _Right == &nil) return _Left; 

    TOrg* pOrg = dynamic_cast<TOrg*>(&_Left);
    if(!pOrg) return _Left;

    if(dynamic_cast<TOrg*>(&_Right))
        ((TOrg*)&_Right)->parent = pOrg;//部门
    else 
        ((TEmp*)&_Right)->org = pOrg;    //职员
    
    return _Left;
}
//指定组织的负责人
TNode & operator >> (TNode & _Left , TNode & _Right)
{
    if(&_Left == &nil || & _Right == &nil) return _Left; 

    TEmp* pEmp = dynamic_cast<TEmp*>(&_Left);
    if(!pEmp) return _Left; 

    ((TOrg*)&_Right)->manager = pEmp;
    
    return _Left;
}
TNode & UpNode(TNode & _Node)
{
    //空节点不处理
    if(&_Node == &nil ) return _Node; 

    if(dynamic_cast<TOrg*>(&_Node))
    {
        //部门
        return ((TOrg*)&_Node)->parent ? *((TOrg*)&_Node)->parent : nil;
    }
    else 
    {
        //职员
        TOrg * org = ((TEmp*)&_Node)->org;
        while(org)
        {
            if(org->manager && org->manager != &_Node) return *org->manager;
            org = org->parent;
        }
        return nil; 
    }
}
//增加上下文节点
TNode & pushContext(map<wstring, TNode *> & context,TNode * base)
{
    context.insert(pair<wstring, TNode *>(base->name,base));
    return *base;
}
//上下文查找
TNode & findContext(map<wstring, TNode *> & context,wchar_t * name)
{
    if(context.find(name)==context.end()) return nil;
    return *context[name];
}
//增加人员节点
TNode & pushEmps(deque<TEmp>& emps,TEmp & emp)
{
    emps.push_back(emp);
    return emps[emps.size() - 1];
}
//增加组织节点
TNode & pushOrgs(deque<TOrg>& orgs,TOrg & org)
{
    orgs.push_back(org);
    return orgs[orgs.size() - 1];
}

/*
    平常的程序语义一般为值代换,宏则提供了形式代换的能力,增强了程序的
表现能力,但也带来许多付作用(意想不到的作用),所以很多教科书和新的语义
都提倡限制宏的作用。
    为减少宏的付作用,在书写宏时一般要遵循一些约定:
    1、宏参量在使用时,一般要用括号括起来,使其保持整体性,同时式子本身也要括号
       扩起来。
       //error define
       #define mul(x,y) x * y // 当x是a+b时,就与原意不符了。
       //normal define
       #define mul(x,y) ((x) * (y))
    2、宏的每一个参量,一般在表达式里只能使用一次。
      这是因为宏参量可能会用有付作用(这里指对状态的修改)的式子替换,替换后这样
      的付作用会执行多次。
      #define ↘ρ(x) (context.insert(pair<wstring, TNode *>((x)->name,&x)),&x)
        如果用这个宏替换正文中对应的宏,你会发现,程序执行后所有的人员和部门都
      会有三条记录,而不是一条。这是因为对于↘ρ(e(张三))这样的语句,宏参数x所
      替换的e(张三),包含了创建与压栈的动作,宏替换后,这样的动作被重复执行了三
      次,而不是预想中的一次。
        要避免出现这样情况,宏变量在应用时尽可能只使用一次,必要时可以创建辅助
      函数来避免这样的情况,这也是本文中出现多个辅助函数的原因。
    3、当可预知宏参量可以是语句的时候,除非有特异的需求,需要用大括号把变量封
       闭起来,防止替换后声明外泄等一些问题。

    以下定义的宏,是尽可能让结构语义形象化、图形化。与计算语义不一样,结构本身是一个几何的概念。
*/
//创建职员节点
#define e(x) pushEmps(emps,TEmp(L###x))
//创建组织节点
#define d(x) pushOrgs(orgs,TOrg(L###x))
//上下文压栈
#define ↘ρ(x) pushContext(context,&(x))
//上下文查找
#define ρ(x) findContext(context,L###x)
//对于组织,指定组织的上下级关系;对于职员,指定职员所在的组织
#define ←  <<
//指定组织的负责人
#define →    >>
//对于职员,标示职员的上级;对于组织,标示上一级组织
#define ↑(x) UpNode(x)

int _tmain(int argc, _TCHAR* argv[])
{
    //这是需要的,否则printf语句不好好输出中文。
    setlocale( LC_ALL, "" );

    /*
            vector对象在做增加元素操作的时候,当数据块容量不够时,会重建数据块。在程序
        上就表现为引用失效。
        例子:
        vector<TEmp> emps;
        emps.push_back(TEmp(L"x1"));
        TEmp & e1 = emps[0];
        emps.push_back(TEmp(L"x3"));
        _tprintf(L"%s",e1.name); //插入"x3"后,e1可能会失效
            而在下面的代码,会频繁使用引用操作,所以使用deque代替vector
            由于上下文引用的不是map的元素,而是真实的节点对象,所以map插入操作是否引起
        本身元素的移动,已经是无关紧要了。
    */
    deque<TEmp> emps; 
    deque<TOrg> orgs;
    map<wstring, TNode *> context; //上下文

    //创建职员
    ↘ρ(e(张三));
    ↘ρ(e(李四));
    ↘ρ(e(王五));
    ↘ρ(e(赵六));
    ↘ρ(e(周七));
    ↘ρ(e(李八));
    ↘ρ(e(孙九));
    ↘ρ(e(张十));

    //创建业务部
    ↘ρ(d(业务部))← ↘ρ(d(业务一部))← ↘ρ(d(业务二部));

    //指定业务部的职员
    ρ(业务一部)← ρ(张三)← ρ(李四);
    ρ(业务二部)← ρ(王五)← ρ(赵六);

    //创建其他部门,并指定职员
    ↘ρ(d(财务部))← ρ(周七);
    ↘ρ(d(办公室))← ρ(李八);
    ↘ρ(d(经理室))← ρ(孙九)← ρ(张十);

    //创建企业
    ↘ρ(d(大洋贸易))← ρ(经理室)← ρ(业务部)← ρ(财务部)← ρ(办公室);

    //指定企业负责人
    ρ(孙九)→ ρ(大洋贸易)→ ρ(经理室);
    
    //指定其他部门的负责人
    ρ(周七)→ ρ(财务部);
    ρ(李八)→ ρ(办公室);
    ρ(张三)→ ρ(业务部);

    //输出一些结果
    printf("赵六的上司: %S\r\n",↑(ρ(赵六)).name); 
    printf("业务部的上一组织: %S\r\n",↑(ρ(业务部)).name); 
    printf("\r\n");
    printf("部门信息:\r\n");
    for(size_t i=0;i<orgs.size();i++)
    {
        printf("%8S\t负责人: %S\t上级部门: %S\r\n",orgs[i].name,
            orgs[i].manager?orgs[i].manager->name:L"",
            ↑(orgs[i]).name);
    }
    printf("\r\n");

    system("pause");
    exit(1);
}

现在再看看运行的结果:

编程便笺:代码的图形(一)_第1张图片

是否跟想像的一致?

 

你可能感兴趣的:(编程)