essay 浅谈ACM盲区(上)

最近在南邮华为俱乐部做了一次小小的交流,讲的内容是“浅谈ACM盲区”,这里我把主要内容整理出来,如果有不正确的地方欢迎指正。
首先我们来谈谈ACM练习的必要性,正如大家所知,ACM可以:
1.提高编程能力(递归、指针、函数、结构……)
2.学习算法(分治、动态规划、回溯……)
3.锻炼思维
……
但是,不得不对ACMer提醒的是,ACM作为一个竞赛,其具有相当的挑战性,亦具有局限性。如若想全面发展,以下的几个ACM盲区是我们所不能忽视的。
(一)界面友好
即使是在Linux中的开发,也是需要界面友好的,更不用提Windows的界面开发(MFC、Java GUI等)。而在ACM中,一般很少有人去考虑界面的设计以及人机交互。在这里,我做一个简单的提示,可以利用main()函数的参数扩展程序功能。
例如:

void main (int arg0, char** arg1)
{
    if (arg0 == 2 && strcmp (arg1[1], "-help") == 0)
    {
        showHelpInformation();
    }
    //……
}

(二)编程规范
变量和函数的命名规范,一定要用有意义的字符,而且尽量用英文。不能用无意义的字符,或者用拼音。
例如:

double getVariance(int *array,int length);
void encrypt(char *plain,char *key,char cipher);
double stdIcValue,minIcValue;

这里我再举三个例子,来说明变量命名的规范性。例子所实现的功能是对一个三位数的百位、十位、个位进行分解。

int x;
int v=x%10;
int vv=x/10%10;
int vvv=x/100;
printf("%d %d %d ",v,vv,vvv);
int x;
int gw=x%10;
int sw=x/10%10;
int bw=x/100;
printf("%d %d %d ",gw,sw,bw);
int x;
int single=x%10;
int decade=x/10%10;
int hundred=x/100;
printf("%d %d %d ", single,decade,hundred);

显然,第三种写法更符合规范,也让人一目了然。
在这里,我在讲一下变量和函数的命名格式,目前主流的写法是采用驼峰式命名。

//变量:开头字母小写
stdIcValue,textField

//函数:开头字母小写
double getVariance(int *array,int length)
View findViewById(int id)
void setOnClickListener(OnClickListener l)

//类、结构: 开头字母大写 
SQLiteOpenHelper LocationListener

//常量: 全大写
#define INFTY 99999 
const int INFTY=99999;
final int INFTY=99999;

还有一点非常重要的规范是关于缩进,从逻辑上来讲,我们把相互对应的代码块进行相同的缩进。下面是一个典型的错误范例,其功能是判断一个考分是否及格。

if(score>=0 && score<=100)
    if(score>=60) printf("pass\n");
else printf("failed\n")

很显然,下面的else应该与第二个if配对,在这里缩进,虽然代码是正确无误的,但是不符合规范。正确的写法应该如下所示:

if(score>=0 && score<=100)
{
    if(score>=60) printf("pass\n");
    else printf("failed\n");
}

再下面我讲讲关于注释,一般地,我们尽量用英文来保证兼容性。例如下面的递归的例子:

void solve(int *answer,int step,int currentTotal,int *change,int length,int money)
{
    //answer is a stack array, step is the level of recursion
    //currentTotal is the current change numbers
    //change is a const array ([11,17,5,1]), length is the length of const array (4)
    //money is a const of total money (20)
    int cnt,i;
    if(step>length) return;
    if(currentTotal>currentMin) return;
    ……
}

同时,我们可以使用/* */来进行小规模或大规模注释,例如在下面的例子中,我们仅仅希望去除函数中最后一个参数的声明:

void recursion(int *answer,int step/*,int *pcnt*/)
{
//  ……
    recursion(answer,step+1/*,int *pcnt*/);
//  (*pcnt)++;  
}

void main()
{
    int *answer=(int*)malloc(20*sizeof(int))/*,cnt=0*/;
    recursion(answer,0/*,&cnt*/);
//  printf("cnt=%d\n",cnt);
}

最后,关于编程规范,我讲讲程序的可移植性、函数封装与模块耦合,一般函数的封装要保证其逻辑性,并将使用者的权限降至最低。
举个简单的例子吧。

void outputArray(int *array,int length)
{
    int i;
    for(i=0;i<length;i++)
        printf("%d ",array[i]);
    printf("\n");
}
void main()
{
    int array[]={1,2,3,4,5};
    int length=sizeof(array)/sizeof(int);
    outputArray(array,length);
}

这是一个正确的例子,outputArray()函数实现了对数组的输出,其符合逻辑性。而有些人在实现其输出时,将回车符的输出printf(“\n”)写在main()函数中。虽然这样同样能得到正确的结果,但是输出功能应该属于函数outputArray(),放在main()函数中实为不妥,不符合逻辑性。

(三)非实用性方法
ACM中一些“独特”的方法,例如打表法、O(n)排序法等,在今后的工作岗位中,几乎不可能用到。我的建议是,ACM中用用就好,别太当回事即可。
举个打表法的例子:

NOJ 1025 请在从1到某个整数范围中打印出所有的完数来,所谓“完数”是指一个数恰好等于它的所有不同因子之和。其中输入的数字n(1<n<10000)
样例输入
100
5000

样例输出
100: 6 28
5000: 6 28 496

如果不用打表法,很难在规定时间内得出结果。但是,如果打表,则相当简单,下面我给出核心代码。

if(n>=6) printf(" 6");
if(n>=28) printf(" 28");
if(n>=496) printf(" 496");
if(n>=8128) printf(" 8128");
printf("\n");

(四)实用性编程
关于实用性编程,我的建议是,在熟练玩转ACM的基础上,至少掌握一个。常见的实用性编程包括但不局限于以下:
Linux下的进程、通信、数据库、GNOME(或Qt)编程
Android 开发
C++ MFC
LAMP Web开发体系(Linux、Apache、MySQL、PHP)

(五)面向对象编程
在ACM中,很少接触到面向对象编程。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。对面向对象的介绍,我会在下一个博客中进行简介。同时会举出一个生动的例子,供大家参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值