I am LAZY bones ? all linux

分类: '编程相关' 的归档

linux进程间通信──消息队列

linux自古以来就是一个多任务多用户的操作系统,所以linux的进程间通信(Inter-Process Communication──IPC)就显得非常重要了。
IPC是一种标准的Unix通讯机制,目前有以下几种通讯方式:管道(Pipe)、信号量(Semaphore)、互斥体(Mutex)、共享内存(Shared Memory)和消息队列(Message Queue),当然也有其他的方式,比如文件系统和dbus等。
今天我就来介绍一种简单实用的进程间通信方式:消息队列(Message Queue)。
首先说说消息队列的优缺点:
1.消息队列只适用于单台主机的进程间通信,如果是不同主机,需要用socket等其他方式,也就不属于IPC的范畴了。
2.消息队列可以实现异步通信,这似乎是优点,但说是它缺点也是可以的:通讯往往不是实时的。
3.消息队列有大小限制,通常只用于小数据量的发送。系统对用户的大小限制可以通过 ulimit -q 命令进行查询。
4.消息队列可以实现阻塞调用和非阻塞调用。
5.实现简单,且可移植性好。

下面通过一个实例来进行说明,以下文件保存成 ipc_msg.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
 
int main(int argc, char *argv[]){
	int ipc_key,msg_id,msg_len;
	long mtype;
	void * mbuf;
	if(argc==5 && argv[3][0]=='s'){
		ipc_key=atoi(argv[1]);
		mtype=atol(argv[2]);
		msg_len=strlen(argv[4]);
		if ( 0>(msg_id=msgget(ipc_key, 0666|IPC_CREAT)))return 1;
		if ( NULL==(mbuf=malloc(msg_len+sizeof(long)+1)) )return 2;
		memcpy(mbuf, (void *)&mtype, sizeof(long) );
		memcpy(mbuf+sizeof(long), (void *)argv[4], msg_len );
		if ( 0>msgsnd(msg_id, (struct msgbuf *)mbuf, msg_len, 0) )return 3;
		printf("Send Success.n");
	}else if(argc==4 && argv[3][0]=='r'){
		ipc_key=atoi(argv[1]);
		mtype=atol(argv[2]);
		if ( 0>(msg_id=msgget(ipc_key, 0666)))return 1;
		if ( NULL==(mbuf=malloc(4096)) )return 2;
		if ( 0>(msg_len=msgrcv(msg_id,(struct msgbuf *)mbuf, 4000, mtype, IPC_NOWAIT))){
			printf("No message received.n");
			return 3;
		}
		printf("Recv Success.(%d bytes):n",msg_len);
		printf("%sn",(char *)(mbuf+sizeof(long)));
	}else if(argc==3 && argv[2][0]=='c'){
		ipc_key=atoi(argv[1]);
		if ( 0>(msg_id=msgget(ipc_key, 0666)))return 1;
		if ( 0>msgctl(msg_id,IPC_RMID,(struct msqid_ds *)mbuf) )return 2;
	}else{
		printf("usage:"
			"%s key type s message --to send messagen"
			"%s key type r --to receiven"
			"%s key c --to clear queuen"
			,argv[0],argv[0],argv[0]);
	}
	return 0;
}

以上程序,实现了发数据到消息队列和从消息队列收取数据的功能。
第一个参数需要是一个整形数值,表示消息队列的Key;
第二个参数是一个长整形的数值,表示消息的Type,Key+Type 可以唯一确定一个先进先出的消息队列。
第三个参数如果是‘s’则把第四个参数发到指定消息队列,如果是‘r’则从指定消息队列收取消息,并打印。
另外,如果第二个参数是‘c’,则把Key对应的队列删除。
让我们来运行一下试试:

$ gcc -o ipc_msg ipc_msg.c
#编译
$ ipcs -q
 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
#一开始系统中没有消息队列。
$ ./ipc_msg  1 2 s abc
Send Success.
#发送了一个内容为abc的消息
$ ipcs -q
 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x00000001 262144     lily       666        3            1           
#发送了一个消息以后,队列里有消息了,key是1,有3个字节。
$ ./ipc_msg  1 2 r
Recv Success.(3 bytes):
abc
#从消息队列成功收到消息了。
$ ipcs -q
 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x00000001 262144     lily       666        0            0           
#收完以后,空的消息队列仍然存在,不会自动消失。
 
$ ./ipc_msg  1 c
#删除队列
$ ipcs -q
 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
#成功删除了,回到原始状态。

本文以GNU自由文档许可证发表.

python用着太顺手了

(此文纯属自言自语,基本可以忽略,呵呵.)
python用着太顺手了,其实是很久以前就有这种感觉,最近印象比较深的一次就是在做Project Euler的第一题的时候,那题比较简单,要求1000以内所有能被3或5整除的自然数之和.这题其实用什么语言都不复杂,但是用python的话,只需要一行:

sum([n for n in range(1000) if n%3==0 or n%5==0])

接近自然语言的表达看起来好舒服,而且也相当简洁.
然后今天,我又更新了一下gmbox,基本上把CLI重新写了一遍,又有同样的感觉了.gmbox的命令行,分交互式和非交互式两种,刚好用cmd和optparse两个内置模块轻松搞定.而且cmd模块支持欢迎界面/自定义提示符/readline库;optparse支持长短选项和混杂无序的选项,并自动生成帮助界面.真是太爽了.这两种模式加起来才140行左右的代码.去掉文件头,只有120行…
以后继续学习python.哈…

bones7456 WP theme 微调

看到Matrix 67的blog的主题,觉得有一点很不错:就是右侧的最新评论那里,一开始是每个评论只占据一行的位置,只有在鼠标放上去的时候,才会把内容展开.
这样的好处很明显:平时节省页面空间,布局也更漂亮,鼠标放上去的时候显示出该有的信息,也不影响使用.
于是,有样学样,把这一小细节学习了过来(呃..好吧,其实就是抄了过来…).
在实现上,没去动WP带的dynamic_sidebar函数,而是偷懒了一下,在页面基本展开完以后,用js处理了一下style,并添加事件函数.看现在是不是更好看了?
之前我都只敢放5个最新评论,因为再多,就不美观了.现在放10个也不是问题.
如果你也喜欢并想试试本WP主题,可以到这里下载最新的代码.
:-)

谷歌音乐下载器

之前有很多下载baidu mp3的程序,有bash的,java的,python的,其中也包括我这个.
但是baidu的歌曲都是用程序收集自网络的,所以排行榜的歌曲质量就没有保障了,下载下来的歌曲ID3信息可谓一塌糊涂,而且还可能下载到网友自己翻唱的歌或者其他杂七杂八的东西,严重影响我们的听觉神经.
而谷歌(不是google)最近推出了谷歌音乐搜索,联合top100,也推出了类似百度榜单的音乐排行榜.但不同于百度的是,谷歌里的歌曲都是收集整理过的,不会有死链,质量也很不错,而且,对于最终非商业用途的个人用户而言,是不存在版权问题的(但我不确定批量下载下来的有没有版权问题.请用户自行考虑.).
所以我顺势就推出了这个谷歌音乐下载器 .
目前程序还很简陋,没有图形界面,也没有很多可以设置的地方.运行程序只会把”华语新歌”这个榜单的100首歌下载到本地当前目录.所以仅供有兴趣尝鲜的同学测试使用.但是以后,我打算把这个程序做成有图形界面的,可以试听/下载/播放的一个整合工具,哈哈.请大家多多关注吧.

PS:有人说这类工具还是不要发布出来,小范围流传下比较好,因为发布出来以后,很可能遭到google的封杀.这说法其实也有些道理,但是我想想,如果谷歌真的因为这个来封杀我,我也够有面子的,嘿嘿.所以我还是按照Google的Project hosting页面所说的做了: Release early, release often

SQLite简介

SQLite大致就是”SQL lite”,也就是一个微型的SQL解释器.它的赞助商和使用者包括mozilla,adobe,symbian等软件巨头,也算是来头不小了.
说它微型,那么它到底有多微型呢?我们可以看看它的可执行文件的大小,linux版本的打包文件是203.93 KiB,解压以后是350.88 KiB,是不是很微型呢?
除了微型之外,SQLite主要还要以下特点:

  • 无须配置: SQLite不需要安装,直接解压可执行文件即可运行.
  • 没有服务进程: SQLite无须通过TCP/IP等通信协议提交SQL到服务端,处理后再返回结果.
  • 单个数据文件: SQLite将用户数据存于单个普通文件里面.也就是说用户只要有数据文件的读权限,就可以读取所有数据;有写权限也能改变数据.
  • 数据文件可跨平台迁移: SQLite本身是跨平台的,它的数据文件同样也是跨平台的.数据文件和平台的字节序无关,也和CPU的位数无关.直接复制数据文件就可以实现数据迁移.
  • 紧凑: 如上所述,SQLite的运行环境非常小,如果在编译的时候去掉不需要的功能,可以减小至170KiB,用于嵌入式环境也是没问题的.
  • 松散数据类型: 不同于其他数据库的严格的数据类型检查,SQLite在尝试类型转换失败以后,允许在任何表的任何列里面插入任何类型的数据(一个例外是:整型的主键列里面只能存整型数据)
  • 变长的数据记录: 任何文本都是VARCHAR
  • 可读性很高的源码: 普通的程序员都能读懂,关键的变量和函数都有详细的注释.
  • SQL语句编译成虚拟机器语言: SQLite把SQL语句预编译成一种类似机器预言的代码,程序员可以方便得打印出代码及代码的执行结果,这样对debug很有好处.
  • 完全的公开: SQLite的源码可以任意下载使用,没有任何约束和版权(一些文档和测试代码是受开源license保护的).
  • SQL语言扩展: SQLite可以模块化地添加扩展,以增强其功能

说了这么多特点,那么,SQLite究竟怎么使用呢?
在一般的linux发行版下,安装 sqlite 这个包以后,就可以使用 sqlite3 这个命令来创建和处理数据库文件了.windows/DOS则下载这个文件解压到系统目录,mac则是下载这个文件.
安装完毕以后,就可以用

sqlite3 test.db

这个命令来创建一个空的数据库文件了.
然后,你可以用以下命令来建立一个含有两个列的表:

sqlite3 test.db "create table memos(text, priority INTEGER);"
sqlite3 test.db "insert into memos values('deliver project description', 10);"
sqlite3 test.db "insert into memos values('lunch with Christine', 100);"

然后可以这样查询出需要的数据:

sqlite3 test.db "select * from memos where priority>20;"
lunch with Christine|100

很简单吧~
下面展示一下,SQLite在C语言下的用法,同样用的是上面那个test.db,我们建立一个test.c,内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <sqlite3.h>
 
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
  int i;
  for(i=0; i<argc; i++){
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
  return 0;
}
 
int main(int argc, char **argv){
  sqlite3 *db;
  char *zErrMsg = 0;
  char SQLbuffer[1024];
  int rc;
 
  if( argc!=2 ){
    fprintf(stderr, "Usage: %s num\n", argv[0]);
    return 1;
  }
  rc = sqlite3_open("test.db", &db);
  if( rc ){
    fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    return 1;
  }
  sprintf(SQLbuffer, "select * from memos where priority > %s;", argv[1]);
  rc = sqlite3_exec(db, SQLbuffer, callback, 0, &zErrMsg);
  if( rc!=SQLITE_OK ){
    fprintf(stderr, "SQL error: %s\n", zErrMsg);
    sqlite3_free(zErrMsg);
  }
  sqlite3_close(db);
  return 0;
}

gcc -lsqlite3 test.c

进行编译,完成之后,执行

./a.out 30
text = lunch with Christine
priority = 100

怎么样,相当简单吧~

注: 文中部分内容翻译和参考自官方文档和man页.

调srt字幕时间的小脚本

首先,我知道mplayer和很多其他播放都有个类似 -subdelay的开关,可以实现字幕和电影的时间调整。
此脚本和subdelay又部分功能交叉,但是某些特殊的时候,还是用个这个脚本方便。
我当时的是电影一个avi文件,但srt字幕却只找到分CD1和CD2的那种,前半段电影的时间和CD1对上了,但是后半段就没字幕了。
为了解决这个文件,就写了这个脚本,名为 adjsrt.sh
我当时执行了 ./adjsrt.sh 3061 cd2.srt >> cd1.srt 就把第二个字幕的时间整体加上3061秒,然后加在了第一个字幕的后面了,哈哈。
脚本如下:

#!/bin/sh
offset=$1
shift
awk -v offset=$offset '
BEGIN{
	FS="[ :,]+"
}
/^[0-9][0-9]:[0-9]*/ {
	ST=toT($1,$2,$3)+offset;
	ST1=int(ST/3600);
	ST2=int((ST-ST1*3600)/60);
	ST3=ST%60;
	ET=toT($6,$7,$8)+offset;
	ET1=int(ET/3600);
	ET2=int((ET-ET1*3600)/60);
	ET3=ET%60;
	printf("%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n",
		ST1,ST2,ST3,$4,ET1,ET2,ET3,$9);
}
$0 !~ /^[0-9][0-9]:[0-9]*/ {
	print $0
}
 
function toT( hour, min , sec ){
	return hour*3600 + min*60 + sec
}
' $*

python多处理器编程

由于上次做的那个一维随机游走程序,虽然简单,但是大数据的时候很费CPU,而且我注意到我的双核处理器始终只有一核是处于满负荷工作,另一个核的性能没有得到发挥.而且我也试过把同样的程序放到一个8核的服务器上运行,结果解题的速度也只是比我本本快那么一点点,估计也只是那服务器的CPU主频(2GHz)比我的1.86GHz略高而已,完全没有发挥出他8核的优势.
所以马上想到了python有没有多处理机的机制,上网google一下,发现由于python是解释型的语言,而Python解释器使用GIL(全局解释器锁)来在内部禁止并行执行,正是这个GIL限制你在多核处理器上同一时间也只能执行一条字节码指令.猜想这个GIL也是当初为了设计解释器方便而搞的吧.而且据说python 3.0 里面已经改进了,默认有了多处理器编程的库了.但是毕竟现在python3.0还没有流行起来,那么现在有没有变通的方法呢?
当然有~不然我就不会写这文章了嘛~
Parallel Python 这个库,正是为了解决我们的问题而设计的,而且它不仅可以多核处理器协同工作,还可以通过网络集群运行呢,嘿嘿.
下面的中文介绍来自这里:

1 简介

PP 是一个Python模块,提供了在SMP(多CPU或多核)和集群(通过网络连接的多台计算机)上并行执行Python代码的机制。轻量级,易于安装,并集成了其他软件。PP也是一个用纯Python代码实现的跨平台,开放源码模块。

2 功能

* 在SMP和集群上并行执行Python代码
* 易于理解和实现的基于工作的并行机制,便于把穿行应用转换成并行的
* 自动构造最佳配置(默认时工作进程数量等同于系统处理器数量)
* 动态处理器分配(允许运行时改变工作处理器数量)
* 函数的工作缓存(透明的缓存机制确保后续调用降低负载)
* 动态负载均衡(任务被动态的分配到各个处理器上)
* 基于SHA的连接加密认证
* 跨平台移植(Windows/Linux/Unix)
* 开放源代码

3 开发动机

现代Python程序已经广泛的应用在商业逻辑,数据分析和科学计算等方面。其中广泛应用着SMP(多处理器或多核)和集群(通过网络连接的多台计算机),市场需要并行的执行的Python代码。
在SMP计算机上编写并行程序最简单的方法是使用多线程。尽管如此,使用 ‘thread’ 和 ‘threading’ 模块仍然无法在字节码一级实现并行。因为Python解释器使用GIL(全局解释器锁)来在内部禁止并行执行。这个GIL限制你在SMP机器上同一时间也只能执行一条字节码指令。
PP 模块正是为了解决这个问题而来,提供简单的方式实现并行Python应用。 ppsmp 在内部使用 进程 和 IPC (进程间通信)来组织并行计算。并处理了所有内部的细节和复杂性,你的应用程序只需要提交工作任务并取回结果就可以了。这也是编写并行程序的最简单的方法。
为了更好的实现,所有使用 PP 的软件通过网络来连接和协作。跨平台和动态负载均衡使得 PP 可以轻松组织多平台、异构的集群计算环境。

4 安装

任何平台:下载模块压缩包,解压,运行setup脚本:
python setup.py install
Windows:下载和执行安装包。

另外,debian和ubuntu用户,也可以通过apt直接下载安装,包名是 python-pp ,但是由于版本比较老,是 1.5.4 版本的,而最新的是 1.5.6 ,所以官方页面上的示例代码可能运行不了,会出现以下错误提示:

Traceback (most recent call last):
File “testpp.py”, line 46, in
job_server = pp.Server(ppservers=ppservers)
File “/var/lib/python-support/python2.5/pp.py”, line 312, in __init__
raise ValueError(“secret must be set using command-line option or configuration file”)
ValueError: secret must be set using command-line option or configuration file

原因是代码的不兼容性,解决办法就是找到 pp.Server 那行,多加一个参数,如下:

job_server = pp.Server(ppservers=ppservers,secret="")

由于这个库,包装得不错,所以用起来也比较简单,基本上看了示例代码,就会了,使用方面也就不多介绍了,如果有可能的话,我倒是想写个gentoo的ebuild文件,嘿嘿.

给 iPod 转视频的脚本

由于新买了个 iPod touch,这几天认真钻研了下它的视频格式,搞了个nautilus脚本,使用mencoder做后端.
以后想把某个视频文件转成iPod的格式的话,直接在 nautilus 里面右键点文件-脚本-toIpod 就可以搞定了.
脚本的特点:
* 借助mplayer的强大,支持N多的源格式(已测试: avi rmvb mov flv).
* 支持srt/ass格式的外挂字幕.
* 自动缩放画面比例到适合ipod touch的480*320,如果是 ipod shuffle 之类的话,可以自己修改下脚本.
* 可视化的进度提示
* 转换速度较快
* 默认保存到当前目录,可修改脚本,输出到统一目录,方便管理.会自动加上 _ipod.mp4 的后缀名.
使用方法,保存以下脚本到 ~/.gnome2/nautilus-scripts/toIpod ,并加可执行权限…或者这里下载

PS: 如果压缩出来的字幕有乱码,请参照我以前的文章,建个 ~/.mplayer/mencoder.conf 文件,写上一行 subcp=cp936 就好了.

#!/bin/bash
#filename: ~/.gnome2/nautilus-scripts/toIpod
#Copyright (c) 2008 bones7456 (bones7456<A>gmail<D>com)
#License: GPL
#version 20081101
#用于将视频转成 ipod touch / iphone 格式.右击文件使用
 
SAVEDIR=`pwd`
#SAVEDIR="/data/movie"
 
INFILE="$1"
PWD=`pwd`
CMD="mencoder -of lavf -lavfopts format=mp4 -oac lavc -ovc lavc "
CMD+="-lavcopts aglobal=1:vglobal=1:vcodec=mpeg4:vbitrate=600:acodec=libfaac:abitrate=128 "
CMD+="-af lavcresample=22050 -vf dsize=480:320:0,scale=0:0,expand=480:320,harddup -ofps 25 -srate 22050 "
P="没有找到对应的字幕."
if [[ -f "${INFILE%.*}.srt" ]];then
	CMD+=" -sub ""\"$PWD/${INFILE%.*}.srt\""
	P="找到字幕文件: ""${INFILE%.*}.srt"
elif [[ -f "${INFILE%.*}.ssa" ]];then
	CMD+=" -sub ""\"$PWD/${INFILE%.*}.ssa\""
	P="找到字幕文件: ""${INFILE%.*}.ssa"
elif [[ -f "${INFILE%.*}.aas" ]];then
	CMD+=" -sub ""\"$PWD/${INFILE%.*}.aas\""
	P="找到字幕文件: ""${INFILE%.*}.aas"
fi
 
CMD+=" -o ""\"$SAVEDIR/${INFILE%.*}_ipod.mp4\""" ""\"$PWD/$INFILE\""
P+="\n\n保存目录: $SAVEDIR"
P+="\n\n是否继续?"
#echo "$CMD" >> ~/toIpod.log
if ! zenity --question --text "$P" ; then
	exit 0;
fi
 
eval "$CMD 2>&1" |\
while read line; do echo $line |\
awk -F '[ :\(\)%]+' '/^Pos/{print "# 速度:",$5,"(点\"取消\"转入后台运行.)";print $4}' ; done |\
zenity --progress --title "正在转换..." --percentage=0 --auto-close --width=500

截图:
工作截图

google code jam

玩了下google编程大赛,我那轮3道题目,题目球猫帖这里了,其中的第3,超烦,算什么打苍蝇的概率,算的累死了,为了不让自己的程序白写,贴在这里晒晒,嘿嘿.
PS: 初学python,发现真好用…

  1. #!/usr/bin/env python
  2. # -*- encoding: utf-8 -*-
  3. import math
  4.  
  5. def sg(x,R):
  6.     '''求弓形面积'''
  7.     return math.acos(x/R)*(R**2)-x*math.sqrt(R**2-x**2)
  8.  
  9. inf=file('C-large.in','r')
  10. outf=file('C-large.out','w')
  11. casenum=int(inf.readline())
  12. for c in range(0,casenum):
  13.     tmp=inf.readline()
  14.     f=float(tmp.split(' ')[0])
  15.     R=float(tmp.split(' ')[1])
  16.     t=float(tmp.split(' ')[2])
  17.     r=float(tmp.split(' ')[3])
  18.     g=float(tmp.split(' ')[4])
  19.  
  20.     TotalArea=math.pi*R**2/4.0
  21.     A=0
  22.     fn=0
  23.     r=r+f
  24.     t=t+f
  25.     g=g-2*f
  26.     if g &gt; 0:
  27.         i=0
  28.         while i*(g+r*2.0)+r &lt; (R-t):
  29.             x=i*(g+r*2.0)+r
  30.             xp=x+g
  31.             j=0
  32.             Yi=math.sqrt((R-t)**2-x**2)
  33.             try:
  34.                 Yip=math.sqrt((R-t)**2-xp**2)
  35.             except:
  36.                 Yip=0
  37.             while j*(g+r*2.0)+r  0:
  38.                     fn+=nfn
  39.                     j+=nfn
  40.                     #print "c=",c,"i=",i,"j=",j,nfn
  41.                     continue
  42.  
  43.                 if xp &lt; R-t:
  44.                     if yp  Yip and yp &lt;= Yi:
  45.                         if y  Yi
  46.                         if y &lt; Yip:
  47.                             A+=(sg(x,R-t)-sg(xp,R-t))/2.0-g*y
  48.                             #w="3a.不规则"
  49.                         elif y &lt; Yi:
  50.                             A+=(sg(y,R-t)-sg(Yi,R-t))/2.0-(Yi-y)*x
  51.                             #w="3b.不规则"
  52.                         else: #这个是不可能的
  53.                             print "c=",c,"i=",i,"j=",j,"y=",y,"yp=",yp,"Yi=",Yi,"Yip=",Yip,"x=",x,"xp=",xp,"R-t=",R-t #,w
  54.                 else:
  55.                     if yp &lt;= Yi:
  56.                         A+=(sg(y,R-t)-sg(yp,R-t))/2.0-g*x
  57.                         #w="4.下不规则"
  58.                     else:
  59.                         A+=(sg(y,R-t)-sg(Yi,R-t))/2.0-(Yi-y)*x
  60.                         #w="5.三角不规则"
  61.  
  62.                 j+=1
  63.             i+=1
  64.     A+=fn*g*g
  65.     #print TotalArea,A
  66.     outf.write('Case #%d: %0.6f\n'%(c+1,(1-A/TotalArea)))
  67. inf.close()
  68. outf.close()

C 语言 IQ 题..

http://linuxfire.com.cn/~alecs/fun/xiaoming.txt

小明初学 C 语言,写了个程序,想打印 99 个 ‘#’. 程序如下:

int i, n=99; main() { for(i=0; i<n ; i--) { printf("#"); } }

但是这个程序是错的.

Question 1: 请大家帮他改正, 但只允许 1) 删除一个字符 or 2) 增加一个字符 or 3) 改变一个字符
Question 2: 同样的限制条件, 使得只打印一个 ‘#’
Qeustion 3: 同样的限制条件, 打印 100 个 ‘#’

这个题目,粗看无聊,仔细想想还挺有意思的,我归纳了下,有这些答案.
下面要帖答案了,要自己思考下的先别往下看了.

点击查看全文 »