做一个高效程序员

  为什么要做高效的程序员?

  这也许是因为“懒”。

  懒有三种表现:

  l  拖延不喜欢的任务

  l  迅速做完不喜欢的任务,以摆脱之

  l  编写某个工具来完成不喜欢的任务,这样以后再也不用做这件事了

  程序员要具备第三种懒,一劳永逸的懒,因为苦干没有光环,别人只会觉得你笨。

  忙碌不等于多产,行动不等于进展,有效编程最重要的工作是思考,人思考的时候常常看上去不是很忙。

 

  我总结了我在工作中用到的一些高效的方法,他们来源于网络,我的同事,还有图书和文档。

  大家可以从加速,专注,自动化和规范性这几方面去理解这些方法是如何促使我们更高效的。

 

容易的开始

  设置你的终端,实现“无限滚屏”,这样你可以随时找到以前显示的信息。

  关掉收到电子邮件的提示音,减少随时被干扰的可能,养成每天定时查看邮件的习惯。

  为每个工作空间分配单一的功能,形成内聚的工作空间,保持注意力,避免大脑情境切换的开销。比如我们可以设置:通信空间,编码空间,文档空间……

  项目组规定一天里不能被打扰的工作时间段,制定交流规则来管理干扰。

  重视代码规范,在非关键之处建立规范,从而在重要的地方倾力发挥你的创造性。

 

自动登录

  要达到的目的:客户机A登录目标机B无需输入密码。

使用ssh-keygen

  单向登录的操作过程:

  l  登录A机器

  l  ssh-keygen –t [rsa|dsa],rsa和dsa是加密方式,任选其一均可,这个命令将会产生公钥(id_rsa.pub,id_dsa.pub)和私钥文件(id_rsa,id_dsa)。

  l  登录目标机B,将产生的公钥文件如id_rsa.pub的内容追加到~/.ssh/authorized_keys文件的最后

  l  大功告成

  如果还需要双向登录不输入密码,你还需要:

  l  设置.ssh目录权限,chmod 700 -R .ssh

  l  设置authorized_keys的权限,chmod 600 authorized_keys,只允许自己有写权限,否则验证无效。

 

使用expect

  expect是建立在Tcl基础上的一个工具,它和Tcl有着相同的语法。

  人们常常用expect自动完成一些需要交互的任务。

  要自动登录,我们可以创建一个可执行的文件,例如myssh,它的内容如下:

#!/usr/bin/expect

 

set timeout -1;

spawn bash;

 

expect "$ " {

    send "ssh oakland\r"

}

 

expect "password: " {

    send "password\r"

}

 

expect "$ " {

    send "ssh gate2\r"

}

 

expect "password: " {

    send "password\r"

}

 

expect "$ " {

    if { [llength $argv] == 0 } {

        send "ssh gui100\r"

    } else {

        send "ssh $argv\r"

    }

}

 

interact

 

  spawn命令启用了一个新的进程执行bash。

  expect语句在返回的信息中查询$符号,一旦成功就向标准输入send一个ssh oakland命令加回车。

  接下来你输入密码,继续登录。

  输入密码的时候,有时候登陆有延迟,如果10秒钟之后依然没有password:字符串出现,expect会退出。我们用set timeout -1让expect永远等下去。

  最后一句interact把这个脚本的控制权交给用户,expect运行完毕之后自动退出进程。

  这样当你使用myssh gui100时,expect就先登录oakland,再登录gate2,然后登陆gui100,大功告成。

  好了,你已经告别了每天上班登录3次到gui100的痛苦了,坐下来喝杯咖啡吧。

 

Shell命令

使用find的-regex选项,利用正则表达式查找文件。

  例如:

  find ./ -regex ‘.*_Model.*’

  注意,这里-regex后面的正则表达式匹配的是整个文件路径,不是单个文件名。

  所以对于一个文件” ./analysis/agiPowerModel.cpp”,你不能用:

  find ./ -regex ‘agi.*_Model.*’

  去匹配它。

 

使用find的-exec选项,针对每个找到的文件执行命令,进一步处理每个找到的文件。

find ./ -regex '.*\.h\|.*\.cpp' -exec sed -i "s/\<tm\([^p].*\)/Agi\1/g" {} \;

{}是占位符,代表前面用find找到的文件名。

最后的”\;”代表这条find命令结束。

为什么要有一个结束符?

因为这是一个Unix命令,你可以把这条命令的结果管道给另一个命令,所以find命令需要这个结束符来知道”exec”部分到哪里为止。

所以这段shell的意思是,先用find找到所有以.h或者.cpp结尾的文件,然后用sed在这些文件中找到以tm开头的字符串,并且忽略tmp这个特殊的变量,用Agi替换tm。比如tmAClass替换之后就成了AgiAClass。其中,在sed的替换表达式中,()符号表示分组,/1表示引用这个组,否则tmAClass会被替换成Agi而不是AgiAClass。

 

使用find+grep的组合,根据其内容进一步过滤找到的文件。

find –name “*.java” –not –regex “.*Db\.java” –exec grep –n “new .*Db” {} \;

所有不是以Db.java结尾的文件,都不应该初始化与数据库相关的类,而我们要找出这些不守规范的文件。

 

使用xargs逐一调用命令,常用于批处理大量文件。

grep -lwr ams_fmtprint actions/agiCharAPL.cpp actions/agiCharCreate.cpp actions/agiChipPartition.cpp actions/agiExportGuiConf.cpp actions/agiImportAPL.cpp | xargs -I '{}' tkdiff '{}'

       -I后面定义的字符’{}’将替换前面的搜索结果。

所以这段shell的意思是,先找到这五个文件中出现过ams_fmtprint的文件。然后对他们逐一调用tkdiff。

换句话说,对tkdiff的调用不是同时执行的,而是执行了一次tkdiff结束之后,再执行下一次。

 

使用alias创建常用命令的别名

少敲几个键总是能让我们更专注于解决问题本身,我们不用每次因为忘记具体的选项去翻技术手册。

我的alias包括去到常用的工作目录,设置环境变量,查看系统状态,更新源代码和回归测试集。

alias rd  'cd /nfs/gui100.home/jdeng/Code/RD'

alias set92 'setenv APACHEROOT /nfs/gui100.home/jdeng/Code/RH9v2; setenv PATH $APACHEROOT/bin:$PATH'

alias showsp 'du -hs ./*'

alias upcode 'cvs update -dP; apmake -j 4; apmake dist -j 4; apmake install dist; tagsrc;'

alias upqa 'cd /home/jdeng/unitQA; rm -rf bin; cvs up -d bin; cvs up -dP; source setup'

 

使用pushd和popd快速切换工作目录。

pushd执行两个动作:将你置于你作为参数传进去的目录下,并将当前目录入栈。pushd是cd的一个替代品。一旦完成了工作,调用popd就可以回到起始的位置。

pushd是一个FILO列表,相当于自助餐厅的一摞碟子。因为他是栈,你可以入栈任意多次,出栈的顺序则恰好相反。

每次pushd被触发时,它都会显示栈中已有的目录。

 

我们用

cd –

回到上次浏览的目录。

 

我们也可以自己用C shell做一个back命令:

alias cd 'set old=$cwd; chdir \!*; set prompt="$user@`hostname`:$cwd-! >"'

alias back 'set back=$old; cd $back; unset $back'

这样你可以用back回到刚才的目录。

 

使用!$, !*, !:[num]引用上一行命令中的字段

!$,引用上一行命令的最后一个字段

!*,引用上一行命令的所有参数字段

!:[num],引用上一行命令的第num个字段

如:

$> ls actions/ reports/

!$,是reports/

!*,是actions/ reports/

!:0,是ls

 

正则表达式

捕获子模式和回退引用。

使用圆括号()来捕获子模式,与圆括号中的模式匹配的字符串会被记录在匹配变量中。

回退引用,匹配用圆括号捕获的子模式的值。

你可以在一次匹配中捕获许多的子模式,所以你就可以回过头来使用\1,\2等等来指代各个被捕获的表达式。

所以,在前面的例子中:

sed -i "s/\<tm\([^p].*\)/Agi\1/g" x.cpp

这一句,捕获的子模式就是([^p].*\),后面的\1是回退引用匹配成功的字符串。

 

另外,假设你要匹配一个用单引号或者双引号括起来的字符串,你可以:

(“[^”]*”|’[^’]*’)

你也可以用回退引用\1:

(‘|”).*?\1

是不是要简单一些?

 

Vim编辑器

进入Vim

在终端输入

vim filename +line_number

可以直接打开文件并定位到某一行。

 

vim filename +/search_pattern

打开文件,并定位到search_pattern出现的地方。

 

配置Vim

每次启动vim时,vim的配置文件~/.vimrc会被自动读取,该文件可以包含一些设置或者脚本。

我们可以配置.vimrc,来实现强力编辑。

下面给出一个例子:

“双引号后面是注释

“去掉有关vi一致性模式,避免以前版本的bug和局限

set nocompatible

 

"tags

    set tags=/nfs/gui100.home/jdeng/Code/RD-2/tags

    set tags+=/nfs/gui100.home/jdeng/Code/xgraph/tags

    set tags+=/nfs/gui100.home/jdeng/Code/qt_src_453/tags

 

"TlistToggle

    let Tlist_Ctags_Cmd="/usr/bin/ctags"

    let Tlist_Inc_Winwidth=0

    let Tlist_Sort_Type='name'

 

"auto indent

    set cindent "cindent will make smartindent useless

    set shiftwidth=4

    set tabstop=4

    set expandtab "use spaces when user type tab

 

"alias

    nmap <F3> :w<cr>

    nmap <F4> :wq<cr>

    nmap <F5> :w!<cr>:!%<cr>

    nmap <F6> :'a,. d<cr>

    nmap <F7> :'a,. y<cr>

    nmap <F8> <C-w><C-w>

    nmap <F9> :TlistToggle<CR>

 

"backspace

    set backspace=eol,start,indent

 

"detect file type

    filetype on

 

"command line history

    set history=1000

 

    syntax on

    set ruler

 

"match {}

    set showmatch

 

    if has("vms")

      set nobackup   " do not keep a backup file, use versions instead

    else

      else

      set backup            " keep a backup file

    endif

 

    set clipboard+=unnamed

    set backup

    set backupdir=/home/jdeng/.vim/backup

    set directory=/home/jdeng/.vim/temp   "where to put swap file

    set runtimepath+=/home/jdeng/.vim

 

"automatically open and close the popup menu/preview window

    au CursorMovedI,InsertLeave * if pumvisible() == 0|silent! pclose|endif

    set completeopt=menuone,menu,longest,preview

 

我觉得比较有用的是alias,它让我们可以敲更少的键去完成一项功能。

auto indent让我们更少去操心缩进的问题,这样我们更专注于编程本身。

 

定位光标

在命令模式下:

$,将光标移动到行尾

^,将光标移动到这一行头一个字符处。

0,将光标移动到行首。

gm,将光标移动到行中间。

f{char},把光标定位到,这一行,当前光标后面的{char}处。

F{char},把光标定位到,这一行,当前光标前面的{char}处。

,和;符号重复F和f命令,‘,’向前定位,‘;’向后定位。

M,将光标定位在当前页面的中间。

L,将光标定位在当前页面最下面。

H,将光标定位在当前页面最上面。

 

命令宏

q{0-9a-zA-Z"},即q后面加一个寄存器名字,我比较喜欢用qq,因为键入很方便。

qq是用来记录动作的,它就像一个录像机,录制你的动作,在需要时再放映出来。

按下qq,开始录制,页面下显示recording,然后你开始操作,完了之后按Esc退回命令模式,再按一下q表示录制完毕。

然后,你就可以用@q放映这些动作了。

这个“录像机”避免了我们很多重复的劳动。当你要处理很多行的数据时,100@q可以执行录制动作100次,大大加快了我们的速度。

 

多重剪贴板

操作系统限制我们只有一个剪贴板,但是Vim支持多重剪贴板(也叫寄存器)。

当你遇到一个任务,需要从一个文件中复制粘贴一些不连续的内容到另一个文件,多数人会复制,然后跳转到另一个文件,再跳转,再复制。你花费了太多时间在环境的切换。如果使用多重剪贴板,你可以一次性复制粘贴所有内容到另一个文件。成批的复制粘贴比反复多次复制粘贴快得多。

Vi中:

["x]yy,将当前行的内容粘贴到寄存器x中,x包括a-zA-Z0-9.%#:-"

:[range]y[ank] [x],将{range}的内容粘贴到剪贴板x。

["x]p,将剪贴板x中的内容粘贴到光标后。

:di[splay] [arg]和:reg[isters] {arg},显示arg中指定的剪贴板内容。

当你习惯了多重剪贴板之后,你会觉得它是一个非常强大的工具,在关键时刻他一定可以帮到你。

 

配合Taglist和ctags查看源代码

taglist插件,可以显示当前文件的“目录”,包括用到的宏,类,函数,等……通过输入:Taglist打开和关闭功能窗口。

 

ctags可以进入到一个类或者函数,是我们阅读代码的好帮手。

在终端输入:

ctags -R --links=no --c++-kinds=+p --fields=+iaS --extra=+q src/

然后我们就可以用vim –t function_name来定位函数。

用vim打开文件以后,我们还可以用ctrl+]进入函数,ctrl+t退出。也可以使用:tag target_name来定位

 

调试

你也许觉得调试是一件没有技术含量的事情,不就是用gdb或者ddd等工具定位错误源吗,任何人都会。

不要过分依赖调试工具,如果有一天,你要远程调试程序,并且网速很慢,或者你的客户出于保密等方面的考虑,不能让你亲临现场,不给你实时的第一手资料,你该怎么办呢?

调试是很容易被忽略的技术,人和人是不同的。

优秀的程序员效率更高,他们用更少的时间,找到更多的错误,他们更正确的修改错误,并且更少引入新的错误。

这是怎么做到的呢?

首先是要调整心态。

如果一出问题,你的反应是怎么可能呢?你就败了,一切都有可能,现在它不但可能,而且已经发生了。

不要怕出错,缺陷让你有所收获。

错误的调试方法包括,不去理解程序,凭猜测找出缺陷,这样你可能永远都不知道自己错在哪里,下一次仍然犯相同的错误。出错并不可怕,可怕的是回回都犯相同的错。

那么科学的调试方法是什么呢?

1.        将错误的状态稳定下来,让错误可再现。这样我们可以通过可重复的试验收集数据。

2.        理解程序,确定错误来源。

a)        收集产生缺陷的相关数据,分析并构造假设

b)        证实或者证伪你的假设,通过试验,测试或者检查代码

c)        对假设做出结论

3.        修补缺陷

4.        对修补进行测试,增加测试到QA regression,让他们有能力检测出这个故障

5.        查找并修改类似的错误

在稳定错误状态时,强迫你自己隔离引发bug的环境,最好只需要一个步骤bug就显露出来了,这样做常常使你洞见到修正它的方法。

在理解程序的时候,一个非常简单又特别有用的技术是向别人解释它。在这个过程中,你必须详细陈述你在编写代码时觉得想当然的事,这个过程中,你的条理越来越清晰,你可能突然发现错误在哪儿。别停下,证明你的假设。

如果修补一个错误你费了很大的劲,问问你自己为什么?你是否能做点什么让下次修正类似的bug容易一些。

你可以建立一个自己犯过的错误类型检查表,每次修bug的时候统计一下,不断警醒自己,磨练自己的弱项,从错误中吸取营养。

最后,我要说的是,良好的思维很重要,但在一些情况下,思维无法取代优秀的调试器,优秀的工具加上良好的思维才是最为有效的组合。

 

使用crontab让琐碎的工作自动化

crontab创建周期性执行的命令。

键入:

crontab –e

开始编辑你的任务,终端显示:

20 0 * * * /home/jdeng/bin/update_source_code

意思是每天0点20分开始升级源代码。

 

用cvs创建你的宠物项目

把有用的小工具,或者你自己的小项目放进SCCS,这样你再也不会弄丢你的“宝贝”了。

输入:

cvs –d /home/jdeng/cvs_root_directory init

初始化目录。

然后,进入一个项目目录:

cd my_pets

再输入:

cvs –d /home/jdeng/cvs_root import project_name ver_tag rel_tag

导入这个项目。

此时,my_pet下的项目将会导入/home/jdeng/cvs_root/project_name中。

在稍后的阶段,你可以用cvs add和cvs commit等命令,将新的文件加入到这个版本库中,大功告成。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值