东北小蟹蟹

如何写一份工整的代码?
代码最重要的,是正确性。前两个重要的,是正确性和易读性。如果想提高后者,不如看看这篇?
扫描右侧二维码阅读全文
17
2019/08

如何写一份工整的代码?

代码最重要的,是正确性。前两个重要的,是正确性和易读性。如果想提高后者,不如看看这篇?

这是东北小蟹蟹的第1篇文章。
要想成为一名优秀的码农(好了我知道你不想),首先就得从代码开始说起。
那么,如何写一份优秀的代码?
代码的正确性自然是最主要的。但这里我并不想谈论这个,今天我主要想谈的是代码的易读性(工整性),用计算机术语说就是代码风格,简称马蜂码风。
码风究竟有多主要?首先来看这样一个例子:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
using namespace std;struct node {int data,rev,sum;node *son[2],*pre;bool judge();bool isroot();void pushdown();void update();void setson(node *child,int lr);}lct[233];int top,a,b;node *getnew(int x){node *now=lct+ ++top;now->data=x;now->pre=now->son[1]=now->son[0]=lct;now->sum=0;now->rev=0;return now;}bool node::judge(){return pre->son[1]==this;}bool node::isroot(){if(pre==lct)return true;return !(pre->son[1]==this||pre->son[0]==this);}void node::pushdown(){if(this==lct||!rev)return;swap(son[0],son[1]);son[0]->rev^=1;son[1]->rev^=1;rev=0;}void node::update({sum=son[1]->sum+son[0]->sum+data;}void node::setson(node *child,int lr){this->pushdown();child->pre=this;son[lr]=child;this->update();}void rotate(node *now){node *father=now->pre,*grandfa=father->pre;if(!father->isroot()) grandfa->pushdown();father->pushdown();now->pushdown();int lr=now->judge();father->setson(now->son[lr^1],lr);if(father->isroot()) now->pre=grandfa;else grandfa->setson(now,father->judge());now->setson(father,lr^1);father->update();now->update();if(grandfa!=lct) grandfa->update();}void splay(node *now){if(now->isroot())return;for(;!now->isroot();rotate(now))if(!now->pre->isroot())now->judge()==now->pre->judge()?rotate(now->pre):rotate(now);}
node *access(node *now){node *last=lct;for(;now!=lct;last=now,now=now->pre){splay(now);now->setson(last,1);}return last;}void changeroot(node *now){access(now)->rev^=1;splay(now);}void connect(node *x,node *y){changeroot(x);x->pre=y;access(x);}void cut(node *x,node *y){changeroot(x);access(y);splay(x);x->pushdown();x->son[1]=y->pre=lct;x->update();}
int query(node *x,node *y){changeroot(x);node *now=access(y);return now->sum;}int main(){scanf("%d%d",&a,&b);node *A=getnew(a);node *B=getnew(b);//连边 Linkconnect(A,B);//断边 Cutcut(A,B);//再连边orz Link again
connect(A,B);printf("%d\n",query(A,B)); return 0;}

这是一篇来自于A+B的恶搞题解(传送门),其中运用了lct求解a+b。原文中的码风很好,这是我魔改的哈。可以看到,这样的代码显得有种让人窒息的感觉,并且代码编写者在之后的查找,修改代码的过程中会很麻烦。因此,这样的代码就不是好代码,看起来乱糟糟的……
原文中的码风就很好,像这样

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
using namespace std;

struct node 
{
    int data,rev,sum;
    node *son[2],*pre;
    bool judge();
    bool isroot();
    void pushdown();
    void update();
    void setson(node *child,int lr);
}lct[233];
int top,a,b;

node *getnew(int x)
{
    node *now=lct+ ++top;
    now->data=x;
    now->pre=now->son[1]=now->son[0]=lct;
    now->sum=0;
    now->rev=0;
    return now;
}

bool node::judge(){return pre->son[1]==this;}

bool node::isroot()
{
    if(pre==lct)return true;
    return !(pre->son[1]==this||pre->son[0]==this);
}

void node::pushdown()
{
    if(this==lct||!rev)return;
    swap(son[0],son[1]);
    son[0]->rev^=1;
    son[1]->rev^=1;
    rev=0;
}

void node::update(){sum=son[1]->sum+son[0]->sum+data;}

void node::setson(node *child,int lr)
{
    this->pushdown();
    child->pre=this;
    son[lr]=child;
    this->update();
}

void rotate(node *now)
{
    node *father=now->pre,*grandfa=father->pre;
    if(!father->isroot()) grandfa->pushdown();
    father->pushdown();now->pushdown();
    int lr=now->judge();
    father->setson(now->son[lr^1],lr);
    if(father->isroot()) now->pre=grandfa;
    else grandfa->setson(now,father->judge());
    now->setson(father,lr^1);
    father->update();now->update();
    if(grandfa!=lct) grandfa->update();
}

void splay(node *now)
{
    if(now->isroot())return;
    for(;!now->isroot();rotate(now))
    if(!now->pre->isroot())
    now->judge()==now->pre->judge()?rotate(now->pre):rotate(now);
}

node *access(node *now)
{
    node *last=lct;
    for(;now!=lct;last=now,now=now->pre)
    {
        splay(now);
        now->setson(last,1);
    }
    return last;
}

void changeroot(node *now)
{
    access(now)->rev^=1;
    splay(now);
}

void connect(node *x,node *y)
{
    changeroot(x);
    x->pre=y;
    access(x);
}

void cut(node *x,node *y)
{
    changeroot(x);
    access(y);
    splay(x);
    x->pushdown();
    x->son[1]=y->pre=lct;
    x->update();
}

int query(node *x,node *y)
{
    changeroot(x);
    node *now=access(y);
    return now->sum;
}

int main()
{
    scanf("%d%d",&a,&b);

    node *A=getnew(a);
    node *B=getnew(b);
    //连边 Link
        connect(A,B);
    //断边 Cut
        cut(A,B);
    //再连边orz Link again
        connect(A,B);
    printf("%d\n",query(A,B)); 
    return 0;
}

这样就很清晰明了了。比如说我想找splay函数,在前面那个代码中你可能十秒钟才能勉强找出来(甚至比这还要长),但后者你顶多四五秒也能找到。好,接下来我们就来看些码风的大忌:

码风规则

不加Tab

Wrong Example

#include <iostream>
using namespace std;
int main()
{
cout<<"Hello!"<<endl;
return 0;
}

Tab是编程员必须养成的习惯,运用Tab你可以轻松的找出一条语句在哪个分支下。

#include <iostream>
using namespace std;
int a=233,b=666;
int main()
{
if(a)
{
if(b)
{
for(int i=0;i<a;i++)
{
if(a)
{
}
cout<<"blahblahblah"<<endl;
}
}
}
}

好了,请0.5s中告诉我,那条输出语句在哪个分支里。

#include <iostream>
using namespace std;
int a=233,b=666;
int main()
{
    if(a)
    { 
        if(b)
        {
            for(int i=0;i<a;i++)
            {
                if(a)
                {
                }
                cout<<"blahblahblah"<<endl;
            }
        }
    }
}

这下,你肯定能轻易看出,这条语句在循环里。:)
不过我想说普通的代码编辑器应该换行时自动出现Tab吧。。。

哦,对了,不加Tab的话,cpp还好。要是python.....

过度压行

Wrong Example

#include <iostream>
using namespace std;int main(){cout<<"Hello!"<<endl;return 0;}

这简直比不加Tab还可恶。。。
当然现实生活中没有恨不得把宇宙浓缩成一行的。像这样的:

if(a!=b&&a!=c&&a!=d&&a!=e&&a!=f&&a!=g&&b!=c&&b!=d&&b!=e&&b!=f&&b!=g&&c!=d&&c!=e&&c!=f&&c!=g&&d!=e&&d!=f&&d!=g&&e!=f&&e!=g&&f!=g)

也叫过度压行,俗称搭火车。
如果你真的得这么写,可以这样:

if(a!=b&&a!=c&&a!=d&&a!=e&&a!=f&&a!=g
       &&b!=c&&b!=d&&b!=e&&b!=f&&b!=g
             &&c!=d&&c!=e&&c!=f&&c!=g
                   &&d!=e&&d!=f&&d!=g
                         &&e!=f&&e!=g
                               &&f!=g)

如果是Python,需要换行符\

if a==b \
   b==c:

大长头文件

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <priority_queue>
#include <map>
#include <bitset>
#include <climits>
int main()
{
    cout<<"Hello world!"<<endl;
    return 0;
}

这种程序会给人一种头重脚轻的感觉。。。。
你自己问一下自己:这个头文件对程序有没有用?

如果程序比较复杂,头文件不得已要很多,可以用万能头。
不过程序能复杂到这种地步,就不会给人头重脚轻的感觉了233

(未完待续,将来发现我会补充)

码风习惯

大括号,换不换行?

这算是争论最久,也最激烈的关于码风的习惯问题了。我的习惯是换行,如果你喜欢让一行充实一点(别充实太多),也可以不换。这个得看喜好了233

万能头,用不用?

万能头2MB这事吧,真没啥大不了的。想用就尽管用。

但也有例外,NOIP最好别用,否则可能会翻车。具体我没试过……

循环变量,在哪定义?

c++支持循环变量在括号中定义。:)那对于啥都想往for括号里装的我,当然在括号中定义啦!

这就是我弃C投草的重要原因。(逃跑.gif)

普通变量,在哪定义?

强烈建议定义为全局变量!即主函数外。这个我亲自体验过,NOIP中,如果你把变量定义为在主函数中的局部变量——蜜汁翻车,你值得拥有。

命名空间std,用不用?

std::能让代码显得高逼格一些。

但是!!!!!!!!
仅限于短代码。

具体为啥,你懂哒~

目前应该就是这些了。同上,将来我会更。

Last modification:October 1st, 2019 at 12:18 pm
东北小蟹蟹已经穷的叮当都响不了啦!!qwq

Leave a Comment