我的大学

辍学经历

在很久很久之前,我在上了高中一年之后。由于成绩较差,以及当时心态爆炸。之后又由于很多原因,就离开了高中。后续过了段时间,虽然被家里人安排去初三继续复读,但始终郁郁不得志。

最后经过深思熟虑之后,决定辍学去打工。

之后陆续颠沛流离,基本上大多数稍微简单的工作我都做过,比如:保安、网管、普通工厂、电子厂以及装修工人,有段时间还靠游戏打金赚钱,用来保证自己不被饿死。如同“人间失格”一般,我变成了一个只会“活着”的机器。当然,虽然看似这段经历很惨,但是我并没有针对辍学这件事特别后悔过。唯一最后悔的事情,就是没有早点学习编程。

从小学三年级第一次见到电脑被震撼到以来,我一直被吸引着,那是无与伦比的吸引力,甚至超越了中二和现实的引力。不论是最早的打游戏,后来玩服务器,然后现在一直以来的编程,都是如此。

后来,当我厌倦了随波逐流之后,我终于开始了思考。我要做什么?我要成为什么?我所期望的未来是什么样子的?答案不言而喻。

之后,我报了培训班,学习Android,之后开始北漂,一直到现在。

自学考试

虽然,工作了两年多之后,我的技术还可以,完成工作完全没问题。但是,在找工作的时候,却还是很难进去中、大型公司。虽然可以选择小公司,而且普通情况小公司工资反而会更高一点。但是,对于我自身来说,技术和职业发展更重要一点。

之后查询了一些信息,最后决定了走自考这条路。虽然成人教育、远程教育等都可以获得相等的国家承认的学历,但是我除了学历之外,还想通过自考学习和巩固一下自己的科班的基础知识,能学到一些东西是很棒的。

报考大学

自己报考大学和专业的话,肯定是选择计算机相关的专业。虽然很想报考“计算机科学与技术”或者“软件工程”这种专业,但很遗憾的是自考没有相关的专业。最后我就报考了:

  • 计算机及应用(专科)
  • 计算机网络(本科)

未来计划

计划3-5年完成学业,拿到专科和本科毕业证。如果可以的话,后续有机会的话,会考虑上一个全日制的研究生。

出师不利

理想很美好,现实较残酷。虽然我编程能力还可以,但是毕竟已经离开学校好久,对于这种长期类型的学习不太熟悉,而且自己正好有很多事情需要去抉择,导致了挂科。首次报名3月,首次考试7月。首次考试报考科目7门,通过1门,挂科6门。挂科的科目中,有些是去了没考过的,大多是没有去参加的。去参加并没有认真学习过的考试,就算应为临时抱佛脚通过了,对我来说并没有很大的价值。

大学生活

最后,只属于我一个人的大学生活开始了~

时间好快, 转眼间已经学安卓半个月了

已经半个月过去了, 不知道我学的怎么样, 反正我自己感觉差的太远了. (QAQ)

不过, 最近过的很充实, 虽然比较累, 但是感觉每一天都很努力. 如果我能够经常保持这样的状态, 或许在未来的某一天, 我真的会喜欢上自己也说不一定.

不过也渐渐的了解到了安卓, 了解到了Google在安卓的努力, Material Design 也非常赞, 很喜欢那种给人一种很清楚生动的设计.

不过可悲的是, 安卓比web前端更加的碎片化, 各种屏幕适配. Web虽然浏览器不一, 但是已经有了各种优秀的库, 用来轻松解决浏览器兼容的问题, 虽然我现在还没有碰到大的兼容问题(其实是因为, 我只用过虚拟机和自己的手机), 没有碰到平板什么的. 估计之后会适配起来比较麻烦, 至少我在手机上看到的和虚拟机上看到的差距比较大. 只能祈祷了…

前两天休假, 就花了半天的时间看视频, 学习如果做小项目, 结果都太难了, 我只能挑一个简单的做了. 然后就做了一个单页面的2048小游戏, 有空玩玩也不错, 不过UI我也是没法看的下去.

2048 这就是那个游戏了, 有兴趣的可以玩玩, 顺便给点评价, 我想要知道自己还有什么不足的地方.

最后, 明天的世界, 我会正面上自己…

程序设计入门-c语言(第7周编程题)

1
单词长度(4分)

题目内容:

你的程序要读入一行文本,其中以空格分隔为若干个单词,以‘.’结束。你要输出这行文本中每个单词的长度。这里的单词与语言无关,可以包括各种符号,比如“it’s”算一个单词,长度为4。注意,行中可能出现连续的空格。

输入格式:

输入在一行中给出一行文本,以‘.’结束,结尾的句号不能计算在最后一个单词的长度内。

输出格式:

在一行中输出这行文本对应的单词的长度,每个长度之间以空格隔开,行末没有最后的空格。

输入样例:

It’s great to see you here.

输出样例:

4 5 2 3 3 4

时间限制:500ms内存限制:32000kb

[php]
#include <stdio.h>
#include <string.h>

int search46(char *p)//在一个字符串中查找’.’,如果找到返回长度,如果没找到返回null
{
int TmpLength;
char searchString=’.’; //需要查找的字符
if(!strchr(p,searchString))
{
return 0;
}
else
{
char *px = strchr(p,searchString);
int re = px-p; //指针相减,得到含有’.’字符串的字符长度
return re;
}
}

int main(void)
{
//获得字符串
char str[1000];
gets(str);
int flag=0; //判断是否是第一个输出
char *pTmpStr=str;
int TmpLength;//字符串的总长度
TmpLength = search46(pTmpStr);
int length=0;//每个字符段的长度
int i;
char tmp;
for(i=0;i<TmpLength;i++)
{ tmp=str[i];
if(tmp==’ ‘)
{
if(length==0)
continue;
if(flag)
printf(" ");
flag++;
printf("%d",length);
length=0;
continue;
}
length++;
}
if(length!=0)
{
if(flag)
printf(" ");
printf("%d",length);
}

return 0;
}
[/php]

2
GPS数据处理(6分)

题目内容:

NMEA- 0183协议是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA- The National Marine Electronics Associa-tion)制定的一套通讯协议。GPS接收机根据NMEA-0183协议的标准规范,将位置、速度等信息通过串口传送到PC机、PDA等 设备。

NMEA-0183协议是GPS接收机应当遵守的标准协议,也是目前GPS接收机上使用最广泛的协议,大多数常见的GPS接收机、GPS数据处理软件、导航软件都遵守或者至少兼容这个协议。

NMEA-0183协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有$GPGGA、$GPGSA、$GPGSV、$GPRMC、$GPVTG、$GPGLL等。

其中$GPRMC语句的格式如下:

$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50

这里整条语句是一个文本行,行中以逗号“,”隔开各个字段,每个字段的大小(长度)不一,这里的示例只是一种可能,并不能认为字段的大小就如上述例句一样。

字段0:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息

字段1:UTC时间,hhmmss.sss格式

字段2:状态,A=定位,V=未定位

字段3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)

字段4:纬度N(北纬)或S(南纬)

字段5:经度dddmm.mmmm,度分格式(前导位数不足则补0)

字段6:经度E(东经)或W(西经)

字段7:速度,节,Knots

字段8:方位角,度

字段9:UTC日期,DDMMYY格式

字段10:磁偏角,(000 – 180)度(前导位数不足则补0)

字段11:磁偏角方向,E=东W=西

字段16:校验值

这里,“*”为校验和识别符,其后面的两位数为校验和,代表了“$”和“*”之间所有字符(不包括这两个字符)的异或值的十六进制值。上面这条例句的校验和是十六进制的50,也就是十进制的80。

提 示:^运算符的作用是异或。将$和*之间所有的字符做^运算(第一个字符和第二个字符异或,结果再和第三个字符异或,依此类推)之后的值对65536取余 后的结果,应该和*后面的两个十六进制数字的值相等,否则的话说明这条语句在传输中发生了错误。注意这个十六进制值中是会出现A-F的大写字母的。另外, 如果你需要的话,可以用Integer.parseInt(s)从String变量s中得到其所表达的整数数字;而 Integer.parseInt(s, 16)从String变量s中得到其所表达的十六进制数字

现在,你的程序要读入一系列GPS输出,其中包含$GPRMC,也包含其他语句。在数据的最后,有一行单独的

END

表示数据的结束。

你的程序要从中找出$GPRMC语句,计算校验和,找出其中校验正确,并且字段2表示已定位的语句,从中计算出时间,换算成北京时间。一次数据中会包含多条$GPRMC语句,以最后一条语句得到的北京时间作为结果输出。

你的程序一定会读到一条有效的$GPRMC语句。

输入格式:

多条GPS语句,每条均以回车换行结束。最后一行是END三个大写字母。

输出格式:

6位数时间,表达为:

hh:mm:ss

其中,hh是两位数的小时,不足两位时前面补0;mm是两位数的分钟,不足两位时前面补0;ss是两位数的秒,不足两位时前面补0。

输入样例:

$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50

END

输出样例:

10:48:13

时间限制:500ms内存限制:32000kb

[php]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *searchStr(char *p)
{//搜索关键字,并返回指针,没找到返回0
char s[]="$GPRMC";
char *sp=strstr(p,s);
if(sp)
return sp;
return 0;
}

char *getNextPStr(char *p)
{//得到下一个题目要求的字符串,返回指针
char * sp = searchStr(p);
char * pstr = p;
while(1)
{
pstr=pstr+strlen(pstr)+1;
sp=searchStr(p);
if(sp!=p)
return sp;
}
return 0;
}
int xtoi(int num)
{//16进制转10进制
// int a,b,out=0;
// if(num<10)
// return num;
// a=num/10;
// b=num%10;

// out = xtoi(a)*16 + b;
return num*16/10;
}

int chackStr(char *p)
{//检测此语句是否能够通过校验,可以的话1,否则返回0
char *ps = p;
ps++;//跳过$字符
int num;
int pi=0; // 校验和
num = ps[0];
ps++;//第2个字符
while(1)
{
if(ps[0]==’*’)
{
break;
}

num=num^*ps;
ps++;
}
ps++;//将指针指向数字
sscanf(ps,"%x",&pi);
if(num==pi)
return 1;
return 0;

}
char* getOKDate(const char *p)
{//输入验证成功的字符串指针,得到已定位句子的时间,将结果保存
char a[1000];
char b[1000];
char *ps=a;
char *date=b;
strcpy(ps,p);
ps = strchr(ps,’,’);
ps++;
strcpy(date,ps);
strtok(date,".");
ps = strchr(ps,’,’);
ps++;
if(ps[0]==’A’)
return date;
// printf("ok");
return 0;
}
int chackEnd(char *p)
{//检测字符串是否是end
char end[]="END";
if(strcmp(p,end)==0)
return 1;
return 0;
}
void outTime(int i)
{
if(i<10)
printf("0");
printf("%d",i);
}
int main()
{
char str[1000]; //字符串载体
gets(str);

// char str[]="$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50";
char *pstr=str; //活动指针
char *pp=str; //判断指针
char d[30];
char *date=d; //保存时间的字符串
//得到$GPRMC开头语句指针
while(!chackEnd(pstr))
{
if(searchStr(pstr))
{
pp = searchStr(pstr);
if(chackStr(pp))
// printf("success");
//将时间数据保存
if(getOKDate(pp))
date = getOKDate(pp);
}
gets(str);
pstr=str;
}

//得到可以验证通过的最后一个字符串的时间,并且输出
int time,h,m,s;
time = atoi(date);
h = time/10000;
time = time%10000;
m = time/100;
s = time%100;
h += 8;
if(h>=24)
h -= 24;
outTime(h);
printf(":");
outTime(m);
printf(":");
outTime(s);

return 0;
}

[/php]

程序设计入门-c语言(第六周编程题)

1
多项式加法(5分)

题目内容:

一个多项式可以表达为x的各次幂与系数乘积的和,比如:

现在,你的程序要读入两个多项式,然后输出这两个多项式的和,也就是把对应的幂上的系数相加然后输出。

程序要处理的幂最大为100。

输入格式:

总共要输入两个多项式,每个多项式的输入格式如下:

每行输入两个数字,第一个表示幂次,第二个表示该幂次的系数,所有的系数都是整数。第一行一定是最高幂,最后一行一定是0次幂。

注意第一行和最后一行之间不一定按照幂次降低顺序排列;如果某个幂次的系数为0,就不出现在输入数据中了;0次幂的系数为0时还是会出现在输入数据中。

输出格式:

从最高幂开始依次降到0幂,如:

2×6+3×5+12×3-6x+20

注意其中的x是小写字母x,而且所有的符号之间都没有空格,如果某个幂的系数为0则不需要有那项。

输入样例:

6 2

5 3

3 12

1 6

0 20

6 2

5 3

2 12

1 6

0 20

输出样例:

4×6+6×5+12×3+12×2+12x+40

时间限制:500ms内存限制:32000kb

[php]
#include <stdio.h>
#include <math.h>

void outNum(int j,int k)
{
int absk = abs(k);
if(absk==1 && j!=1 && j!=0)
{
printf("x%d",j);
}
else if(absk==1 && j!=0)
{
printf("x");
}
else if(absk==1)
{
printf("%d",absk);
}
else if(j==0)
{
printf("%d",absk);
}
else if(j==1)
{
printf("%dx",absk);
}
else
{
printf("%dx%d",absk,j);
}

}

int main()
{
int num[101] = { (0) };
int cut = 0;
int ent = 0;
int i,j,k;
while(cut < 2)
{
k=0;
scanf("%d %d",&j,&k);
num[j] += k;
if(j==0)
cut++;
}

for(i=100;i>-1;i–)
{
j=i;
k=num[j];
if(k!=0)
{
ent++;
if(cut==2)
{
if(k<0)
printf("-");
outNum(j,k);
cut ++;
}
else
{
if(k<0)
{
printf("-");
outNum(j,k);
}
else
{
printf("+");
outNum(j,k);
}
}
}
}
if(ent==0)
printf("0");

return 0;
}
[/php]

2
鞍点(5分)

题目内容:

给定一个n*n矩阵A。矩阵A的鞍点是一个位置(i,j),在该位置上的元素是第i行上的最大数,第j列上的最小数。一个矩阵A也可能没有鞍点。

你的任务是找出A的鞍点。

输入格式:

输入的第1行是一个正整数n, (1<=n<=100),然后有n行,每一行有n个整数,同一行上两个整数之间有一个或多个空格。

输出格式:

对输入的矩阵,如果找到鞍点,就输出其下标。下标为两个数字,第一个数字是行号,第二个数字是列号,均从0开始计数。

如果找不到,就输出

NO

题目所给的数据保证了不会出现多个鞍点。

输入样例:

4

1 7 4 1

4 8 3 6

1 6 1 2

0 7 8 9

输出样例:

2 1

时间限制:500ms内存限制:32000kb

[php]
#include <stdio.h>

int main()
{
int i,j,k,n;
int x,y;
int nn[100][100];
scanf("%d",&n);
if(n!=1)
{

for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
scanf("%d",&nn[i][j]);
}
}
}
if(n==1)
{
printf("0 0");
return 0;
}
for(i=0;i<n;i++)
{
x=0;y=0;
for(j=0;j<n;j++)
{
if(nn[i][j] > nn[i][y])
{
y=j;
}
}
for(j=0;j<n;j++)
{
if(nn[j][y]<nn[x][y])
x=j;
}
if(nn[i][y]==nn[x][y])
{
printf("%d %d",x,y);
return 0;
}

}
printf("NO");
return 0;
}

[/php]

程序设计入门-c语言(第五周编程题)

1
分解质因数(5分)

题目内容:

每个非素数(合数)都可以写成几个素数(也可称为质数)相乘的形式,这几个素数就都叫做这个合数的质因数。比如,6可以被分解为2×3,而24可以被分解为2x2x2x3。

现在,你的程序要读入一个[2,100000]范围内的整数,然后输出它的质因数分解式;当读到的就是素数时,输出它本身。

输入格式:

一个整数,范围在[2,100000]内。

输出格式:

形如:

  1.  n=axbxcxd

  1.  n=n

所有的符号之间都没有空格,x是小写字母x。

输入样例:

  1.  18

输出样例:

  1.  18=2x3x3
时间限制:500ms内存限制:32000kb

[php]
#include <stdio.h>

int chackSuShu(int n)
{//检测一个数是否是素数
int i;
if(n == 2)
return 1;
for( i =2; i<n; i++)
{
if( n%i ==0)
return 0;
}
return 1;
}
int chackZhiYingShu(int num,int zys)
{//检测一个数能否被某个素数相除
if(num%zys==0)
{
return 1;
}
return 0;
}
int getNextSuShu(int gss)
{//得到下一个素数
int i;
for(i=gss+1;;i++)
{
if(chackSuShu(i))
{
return i;
}
}
}
int getMinZYS(int num)
{//得到最小质因数
int n=2;
while(!chackZhiYingShu(num,n))
{
n=getNextSuShu(n);
}
return n;
}

int main()
{
int num;
int snum;
int next;
//读入一个数
scanf("%d",&num);
//得到第一个质因数
snum = getMinZYS(num);
//输出第一阶段
printf("%d=%d",num,snum);
next = num/snum;
//得到剩余,循环输出
while(next!=1)
{
snum = getMinZYS(next);
printf("x%d",snum);
next = next/snum;
}
}

[/php]

2
完数(5分)

题目内容:

一个正整数的因子是所有可以整除它的正整数。而一个数如果恰好等于除它本身外的因子之和,这个数就称为完数。例如6=1+2+3(6的因子是1,2,3)。

现在,你要写一个程序,读入两个正整数n和m(1<=n<m<1000),输出[n,m]范围内所有的完数。

提示:可以写一个函数来判断某个数是否是完数。

输入格式:

两个正整数,以空格分隔。

输出格式:

其间所有的完数,以空格分隔,最后一个数字后面没有空格。如果没有,则输出一个空行。

输入样例:

1 10

输出样例:

6

时间限制:800ms内存限制:32000kb

[php]
#include <stdio.h>
int chackWanShu(int num)
{
int i,sum=0;
for(i=1;i<num;i++)
{
if(num%i==0)
{
sum += i;
}
}
if(sum==num)
{
return 1;
}
return 0;
}
int main()
{
int min,max;
scanf("%d %d",&min,&max);
int i;
int cut = 1;
for(i=min;i<max;i++)
{
if(chackWanShu(i))
{
if(cut==1)
{
printf("%d",i);
cut++;
}
else
{
printf(" %d",i);
}
}

}
if(cut==1)
{
printf("n");
}
}

[/php]

程序设计入门-c语言(第4周练习题)

1
素数和(5分)

题目内容:

我们认为2是第一个素数,3是第二个素数,5是第三个素数,依次类推。

现在,给定两个整数n和m,0<n<=m<=200,你的程序要计算第n个素数到第m个素数之间所有的素数的和,包括第n个素数和第m个素数。

输入格式:

两个整数,第一个表示n,第二个表示m。

输出格式:

一个整数,表示第n个素数到第m个素数之间所有的素数的和,包括第n个素数和第m个素数。

输入样例:

2 4

输出样例:

15

时间限制:500ms内存限制:32000kb

[php]
#include <stdio.h>

int chackSuShu(int n)
{
int i;
if(n == 2)
return 1;
for( i =2; i<n; i++)
{
if( n%i ==0)
return 0;
}
return 1;
}

int main()
{
int n,m,i,cut=0;
int he=0;
scanf("%d %d", &n, &m);
i=2;
while(cut<=m)
{
if(chackSuShu(i)==1)
{
cut ++;
if(cut>=n && cut <= m){
he += i;
}
}
i++;
}
printf("%d", he);
return 0;
}

[/php]


2
念整数(5分)

题目内容:

你的程序要读入一个整数,范围是[-100000,100000]。然后,用汉语拼音将这个整数的每一位输出出来。

如输入1234,则输出:

  1. yi er san si

注意,每个字的拼音之间有一个空格,但是最后的字后面没有空格。当遇到负数时,在输出的开头加上“fu”,如-2341输出为:

  1. fu er san si yi

输入格式:

一个整数,范围是[-100000,100000]。

输出格式:

表示这个整数的每一位数字的汉语拼音,每一位数字的拼音之间以空格分隔,末尾没有空格。

输入样例:

-30

输出样例:

fu san ling

时间限制:500ms内存限制:32000kb

[php]
#include <stdio.h>

void outNum(int num)
{
switch(num)
{
case 1: printf("yi");break;
case 2: printf("er");break;
case 3: printf("san");break;
case 4: printf("si");break;
case 5: printf("wu");break;
case 6: printf("liu");break;
case 7: printf("qi");break;
case 8: printf("ba");break;
case 9: printf("jiu");break;
case 0: printf("ling");break;
}
}

int chackNum(int num)
{
int n,tmp,out;
tmp = num;
n = tmp %10;
tmp /= 10;
if (tmp>0)
{
out = chackNum(tmp);
outNum(out);
printf(" ");
}
return n;
}

int main()
{
int num,out;
scanf("%d", &num);
if( num < 0 )
{
printf("fu ");
}
num = abs(num);
out = chackNum(num);
outNum(out);
}

[/php]

两道编程题练习

来自 网易云课堂 程序设计入门-c语言

1
奇偶个数(5分)

题目内容:

你的程序要读入一系列正整数数据,输入-1表示输入结束,-1本身不是输入的数据。程序输出读到的数据中的奇数和偶数的个数。

输入格式:

一系列正整数,整数的范围是(0,100000)。如果输入-1则表示输入结束。

输出格式:

两个整数,第一个整数表示读入数据中的奇数的个数,第二个整数表示读入数据中的偶数的个数。两个整数之间以空格分隔。

输入样例:

9 3 4 2 5 7 -1

输出样例:

4 2

时间限制:500ms内存限制:32000kb


[php]
#include <stdio.h>
int main(){
int num;
int js = 0;
int ous = 0;
while(1)
{
scanf("%d",&num);
if(num == -1)
{
break;
}
while(num>2){
num = num – 2;
}
if (num==1)
{
js++;
}
else
{
ous++;
}

}

printf("%d %d",js,ous);
return 0;
}
[/php]


2
数字特征值(5分)

题目内容:

对 数字求特征值是常用的编码算法,奇偶特征是一种简单的特征值。对于一个整数,从个位开始对每一位数字编号,个位是1号,十位是2号,以此类推。这个整数在 第n位上的数字记作x,如果x和n的奇偶性相同,则记下一个1,否则记下一个0。按照整数的顺序把对应位的表示奇偶性的0和1都记录下来,就形成了一个二 进制数字。比如,对于342315,这个二进制数字就是001101。

这里的计算可以用下面的表格来表示:

数字 3 4 2 3 1 5
数位 6 5 4 3 2 1
数字奇偶
数位奇偶
奇偶一致 0 0 1 1 0 1
二进制位值 32 16 8 4 2 1

按照二进制位值将1的位的位值加起来就得到了结果13。

你的程序要读入一个非负整数,整数的范围是[0,100000],然后按照上述算法计算出表示奇偶性的那个二进制数字,输出它对应的十进制值。

提示:将整数从右向左分解,数位每次加1,而二进制值每次乘2。

输入格式:

一个非负整数,整数的范围是[0,100000]。

输出格式:

一个整数,表示计算结果。

输入样例:

342315

输出样例:

13

时间限制:500ms内存限制:32000kb

[php]
#include <stdio.h>
int chacknum(int num)
{
while(num>=2)
{
num -= 2;
}
return num;
}

int main()
{
int num;
int n,out=0,cut =1;
int oi = 1;
scanf("%d",&num);
while( num != 0)
{
n = num%10;
if ( chacknum(n) == chacknum(cut))
{
out += oi;
}

///////////
oi *= 2;
cut ++;
num = num/10;
}

printf("%d", out);
return 0;
}
[/php]

php 验证码学习笔记

我们为什么需要验证码?

  • 需要判断正在执行操作的另一方,是真正的人/或者是非人(机器人/脚本/程序)。

生产验证码的基本步骤?

  1. 产生效验数据
  2. 产生干扰数据
  3. 生成图片
  4. 让用户看到图片
  5. 验证真实性

PHP做验证码主要用GD库来实现。

 

GD

  • resource imagecreatetruecolor(int $width,int $height);
  • 创建一个图片 返回一个图像标示符
  • imagecolorallocate (resource $image, 255, 255, 255);
  • 分配颜色  输入资源标示符+RGB颜色  返回一个标示符
  • // 如果想要显示透明度 可以用 imagecolorallocatealpha() 函数;
  • bool imagefill ( resource $image, int $x, int $y, int $color);
  • 区域填充 在image资源上,开始坐标为x,y 填充 color
  • bool imagesetpixel ( resource $image, int x,int y, int $color);
  • 在image资源上 在x,y坐标产生一个像素的color颜色的点
  • bool imageline ( resourcce $image, int $x1, int $y1, int $x2, int $y2, int $color);
  • 在image资源上,从 x1,y1画到 x2,y2,产生一条颜色color的线段
  • bool imagestring ( resource $image, int $fontsize, int $x,int $y, string $string, int $color );
  • 在image资源上 ,从 x,y开始花上字符串string,并且颜色是color,字体大小是fontsize

其他需要掌握的函数

  •  string substr( string $string , int $start [, int $length ] );
  • 返回 字符串。 从源字符串string上,从start开始 到长度为length处截取 。
  • int rand ( int $min, int $max );
  • 返回min到max直接的随机数。

如果你还要验证的话

  • $_SESSION[]
  •  session 全局变量需要知道
  • bool session_start(void);
  • 创建新会话或者重用现有会话。
  • $_REQUEST[]
  • request 全局变量需要了解

等等 你需要了解更多,请查看 php手册 ,边学习边看手册是一个不错的方法。

最后, 是练习的代码:

[php]
<?php
session_start();
// create image
$image = imagecreatetruecolor(100, 30);
// color image
$bgcolor = imagecolorallocate($image, 255, 255, 255);
// put color
imagefill($image, 0, 0, $bgcolor);
// create pixel
for ($i=0; $i < 200; $i++) {
$x=rand(0,200);
$y=rand(0,30);
$pixcol = imagecolorallocate($image, rand(1,200), rand(1,200), rand(1,200));
imagesetpixel($image, $x, $y, $pixcol);
}

// create line
for ($i=0; $i < 4; $i++) {
$linecolor = imagecolorallocate($image, rand(100,200), rand(100,200), rand(100,200));
imageline($image, rand(0,50), rand(0,30), rand(0,100), rand(0,30), $linecolor);
}

// create unique rand int
for ($i=0; $i < 4; $i++) {
$num = rand(0,9);
$numcolor = imagecolorallocate($image, rand(0,150), rand(0,150), rand(0,150));
$x = ($i+1)*20;
$y = rand(0,15);
imagestring($image, 6, $x, $y, $num, $numcolor);
}

// create unique sting
$yzmyz = ”;
for ($i=0; $i < 4; $i++) {
$strcol = imagecolorallocate($image, rand(0,150), rand(0,150), rand(0,150));
$data=’abcdefghijkmnpqrstuvwxy123456789′;
$str = substr($data, rand(0,strlen($data)-1),1);
$yzmyz .= $str;
imagestring($image, 6, ($i+1)*20, rand(0,15), $str, $strcol);
}

$_SESSION[‘yzmyz’] = $yzmyz;

header(‘Content-type: image/png’);

imagepng($image);

// drop image
imagedestroy($image);
?>
[/php]

ItPP Team

由4个刚刚成年的菜鸟程序员(爱好者)组建的 业余 团队 成立了 , 团队主页 ITPP.TOP 。

InTernet Positive People  在互联网行业快乐的挣扎,这是我们最美好的期望。

当然,我们每个人都有自己的学习和工作,处在实习阶段和在社会上努力的初 成年人 , 是我们的组成。

同样,欢迎 16岁以上能独立思考的喜爱互联网的菜鸟加入。当然,如果你是已经在职的IT成功人士,也可以加入,来一起寻找过去和未来,心中的那一种感情……

以上 by:ETby ;

【酷壳】 函数式编程

当我们说起函数式编程来说,我们会看到如下函数式编程的长相:

  • 函数式编程的三大特性:
    • immutable data 不可变数据:像Clojure一样,默认上变量是不可变的,如果你要改变变 量,你需要把变量copy出去修改。这样一来,可以让你的程序少很多Bug。因为,程序中的状态不好维护,在并发的时候更不好维护。(你可以试想一下如果 你的程序有个复杂的状态,当以后别人改你代码的时候,是很容易出bug的,在并行中这样的问题就更多了)
    • first class functions:这个技术可以让你的函数就像变量一样来使用。也就是说,你的函数可以像变量一样被创建,修改,并当成变量一样传递,返回或是在函数中嵌套函数。这个有点像Javascript的Prototype(参看Javascript的面向对象编程
    • 尾递归优化:我们知道递归的害处,那就是如果递归很深的话,stack受不了,并会导致性能大幅度下降。所以,我们使用尾递归优化技术——每次递归时都会重用stack,这样一来能够提升性能,当然,这需要语言或编译器的支持。Python就不支持。
  • 函数式编程的几个技术
    • map & reduce :这个技术不用多说了,函数式编程最常见的技术就是对一个集合做Map和Reduce操作。这比起过程式的语言来说,在代码上要更容易阅读。(传统过程式 的语言需要使用for/while循环,然后在各种变量中把数据倒过来倒过去的)这个很像C++中的STL中的 foreach,find_if,count_if之流的函数的玩法。
    • pipeline:这个技术的意思是,把函数实例成一个一个的action,然后,把一组action放到一个数组或是列表中,然后把数据传给这个action list,数据就像一个pipeline一样顺序地被各个函数所操作,最终得到我们想要的结果。
    • recursing 递归 :递归最大的好处就简化代码,他可以把一个复杂的问题用很简单的代码描述出来。注意:递归的精髓是描述问题,而这正是函数式编程的精髓。
    • currying:把一个函数的多个参数分解成多个函数, 然后把函数多层封装起来,每层函数都返回一个函数去接收下一个参数这样,可以简化函数的多个参数。在C++中,这个很像STL中的bind_1st或是bind2nd。
    • higher order function 高阶函数:所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出,就像面向对象对象满天飞一样。

 

  • 还有函数式的一些好处
    • parallelization 并行:所谓并行的意思就是在并行环境下,各个线程之间不需要同步或互斥。
    • lazy evaluation 惰性求值:这个需要编译器的支持。表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值,也就是说,语句如x:=expression; (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到 x 中,但是先不管实际在 x 中的是什么,直到通过后面的表达式中到 x 的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。
    • determinism 确定性:所谓确定性的意思就是像数学那样 f(x) = y ,这个函数无论在什么场景下,都会得到同样的结果,这个我们称之为函数的确定性。而不是像程序中的很多函数那样,同一个参数,却会在不同的场景下计算出不 同的结果。所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化。

上面的那些东西太抽象了,还是让我们来循序渐近地看一些例子吧。

我们先用一个最简单的例子来说明一下什么是函数式编程。

先看一个非函数式的例子:

1
2
3
4
int cnt;
void increment(){
    cnt++;
}

那么,函数式的应该怎么写呢?

1
2
3
int increment(int cnt){
    return cnt+1;
}

你可能会觉得这个例子太普通了。是的,这个例子就是函数式编程的准则:不依赖于外部的数据,而且也不改变外部数据的值,而是返回一个新的值给你

我们再来看一个简单例子:

1
2
3
4
5
6
7
8
9
10
def inc(x):
    def incx(y):
        return x+y
    return incx
inc2 = inc(2)
inc5 = inc(5)
print inc2(5) # 输出 7
print inc5(5) # 输出 10

我们可以看到上面那个例子inc()函数返回了另一个函数incx(),于是我们可以用inc()函数来构造各种版本的inc函数,比如:inc2()和inc5()。这个技术其实就是上面所说的Currying技术。从这个技术上,你可能体会到函数式编程的理念:把函数当成变量来用,关注于描述问题而不是怎么实现,这样可以让代码更易读。

Map & Reduce

在函数式编程中,我们不应该用循环迭代的方式,我们应该用更为高级的方法,如下所示的Python代码

1
2
3
name_len = map(len, ["hao", "chen", "coolshell"])
print name_len
# 输出 [3, 4, 9]

你可以看到这样的代码很易读,因为,这样的代码是在描述要干什么,而不是怎么干

我们再来看一个Python代码的例子:

1
2
3
4
5
6
def toUpper(item):
      return item.upper()
upper_name = map(toUpper, ["hao", "chen", "coolshell"])
print upper_name
# 输出 ['HAO', 'CHEN', 'COOLSHELL']

顺便说一下,上面的例子个是不是和我们的STL的transform有些像?

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main() {
  string s="hello";
  string out;
  transform(s.begin(), s.end(), back_inserter(out), ::toupper);
  cout << out << endl;
  // 输出:HELLO
}

在上面Python的那个例子中我们可以看到,我们写义了一个函数toUpper,这个函数没有改变传进来的值,只是把传进来的值做个简单的操作, 然后返回。然后,我们把其用在map函数中,就可以很清楚地描述出我们想要干什么。而不会去理解一个在循环中的怎么实现的代码,最终在读了很多循环的逻辑 后才发现原来是这个或那个意思。 下面,我们看看描述实现方法的过程式编程是怎么玩的(看上去是不是不如函数式的清晰?):

1
2
3
4
upname =['HAO', 'CHEN', 'COOLSHELL']
lowname =[]
for i in range(len(upname)):
    lowname.append( upname[i].lower() )

对于map我们别忘了lambda表达式:你可以简单地理解为这是一个inline的匿名函数。下面的lambda表达式相当于:def func(x): return x*x

1
2
3
squares = map(lambda x: x * x, range(9))
print squares
# 输出 [0, 1, 4, 9, 16, 25, 36, 49, 64]

我们再来看看reduce怎么玩?(下面的lambda表达式中有两个参数,也就是说每次从列表中取两个值,计算结果后把这个值再放回去,下面的表达式相当于:((((1+2)+3)+4)+5) )

1
2
print reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
# 输出 15

Python中的除了map和reduce外,还有一些别的如filter, find, all, any的函数做辅助(其它函数式的语言也有),可以让你的代码更简洁,更易读。 我们再来看一个比较复杂的例子:

计算数组中正数的平均值
1
2
3
4
5
6
7
8
9
10
11
12
13
num =[2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8]
positive_num_cnt = 0
positive_num_sum = 0
for i in range(len(num)):
    if num[i] > 0:
        positive_num_cnt += 1
        positive_num_sum += num[i]
if positive_num_cnt > 0:
    average = positive_num_sum / positive_num_cnt
print average
# 输出 5

如果用函数式编程,这个例子可以写成这样:

1
2
positive_num = filter(lambda x: x>0, num)
average = reduce(lambda x,y: x+y, positive_num) / len( positive_num )

C++11玩的法:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <algorithm>
#include <numeric>
#include <string>
#include <vector>
using namespace std;
vector num {2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8};
vector p_num;
copy_if(num.begin(), num.end(), back_inserter(p_num), [](int i){ return (i>0);} );
int average = accumulate(p_num.begin(), p_num.end(), 0) / p_num.size();
cout << "averge: " << average << endl;

我们可以看到,函数式编程有如下好处:

1)代码更简单了。
2)数据集,操作,返回值都放到了一起。
3)你在读代码的时候,没有了循环体,于是就可以少了些临时变量,以及变量倒来倒去逻辑。
4)你的代码变成了在描述你要干什么,而不是怎么去干。

最后,我们来看一下Map/Reduce这样的函数是怎么来实现的(下面是Javascript代码)

map函数
1
2
3
4
5
6
7
var map = function (mappingFunction, list) {
  var result = [];
  forEach(list, function (item) {
    result.push(mappingFunction(item));
  });
  return result;
};

下面是reduce函数的javascript实现(谢谢 @下雨在家 修正的我原来的简单版本)

reduce函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function reduce(actionFunction, list, initial){
    var accumulate;
    var temp;
    if(initial){
        accumulate = initial;
    }
    else{
        accumulate = list.shfit();
    }
    temp = list.shift();
    while(temp){
        accumulate = actionFunction(accumulate,temp);
        temp = list.shift();
    }
    return accumulate;
};

Declarative Programming vs Imperative Programming

前面提到过多次的函数式编程关注的是:describe what to do, rather than how to do it. 于是,我们把以前的过程式的编程范式叫做 Imperative Programming – 指令式编程,而把函数式的这种范式叫做 Declarative Programming – 声明式编程。

下面我们看一下相关的示例(本示例来自这篇文章 )。

比如,我们有3辆车比赛,简单起见,我们分别给这3辆车有70%的概率可以往前走一步,一共有5次机会,我们打出每一次这3辆车的前行状态。

对于Imperative Programming来说,代码如下(Python):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from random import random
time = 5
car_positions = [1, 1, 1]
while time:
    # decrease time
    time -= 1
    print ''
    for i in range(len(car_positions)):
        # move car
        if random() > 0.3:
            car_positions[i] += 1
        # draw car
        print '-' * car_positions[i]

我们可以把这个两重循环变成一些函数模块,这样有利于我们更容易地阅读代码:

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
from random import random
def move_cars():
    for i, _ in enumerate(car_positions):
        if random() > 0.3:
            car_positions[i] += 1
def draw_car(car_position):
    print '-' * car_position
def run_step_of_race():
    global time
    time -= 1
    move_cars()
def draw():
    print ''
    for car_position in car_positions:
        draw_car(car_position)
time = 5
car_positions = [1, 1, 1]
while time:
    run_step_of_race()
    draw()

上面的代码,我们可以从主循环开始,我们可以很清楚地看到程序的主干,因为我们把程序的逻辑分成了几个函数,这样一来,我们的代码逻辑也会变得几个 小碎片,于是我们读代码时要考虑的上下文就少了很多,阅读代码也会更容易。不像第一个示例,如果没有注释和说明,你还是需要花些时间理解一下。而把代码逻辑封装成了函数后,我们就相当于给每个相对独立的程序逻辑取了个名字,于是代码成了自解释的

但是,你会发现,封装成函数后,这些函数都会依赖于共享的变量来同步其状态。于是,我们在读代码的过程时,每当我们进入到函数里,一量读到访问了一 个外部的变量,我们马上要去查看这个变量的上下文,然后还要在大脑里推演这个变量的状态, 我们才知道程序的真正逻辑。也就是说,这些函数间必需知道其它函数是怎么修改它们之间的共享变量的,所以,这些函数是有状态的

我们知道,有状态并不是一件很好的事情,无论是对代码重用,还是对代码的并行来说,都是有副作用的。因此,我们要想个方法把这些状态搞掉,于是出现了我们的 Functional Programming 的编程范式。下面,我们来看看函数式的方式应该怎么写?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from random import random
def move_cars(car_positions):
    return map(lambda x: x + 1 if random() > 0.3 else x,
               car_positions)
def output_car(car_position):
    return '-' * car_position
def run_step_of_race(state):
    return {'time': state['time'] - 1,
            'car_positions': move_cars(state['car_positions'])}
def draw(state):
    print ''
    print 'n'.join(map(output_car, state['car_positions']))
def race(state):
    draw(state)
    if state['time']:
        race(run_step_of_race(state))
race({'time': 5,
      'car_positions': [1, 1, 1]})

上面的代码依然把程序的逻辑分成了函数,不过这些函数都是functional的。因为它们有三个症状:

1)它们之间没有共享的变量。
2)函数间通过参数和返回值来传递数据。
3)在函数里没有临时变量。

我们还可以看到,for循环被递归取代了(见race函数)—— 递归是函数式编程中带用到的技术,正如前面所说的,递归的本质就是描述问题是什么。

Pipeline

pipeline 管道借鉴于Unix Shell的管道操作——把若干个命令串起来,前面命令的输出成为后面命令的输入,如此完成一个流式计算。(注:管道绝对是一个伟大的发明,他的设哲学就 是KISS – 让每个功能就做一件事,并把这件事做到极致,软件或程序的拼装会变得更为简单和直观。这个设计理念影响非常深远,包括今天的Web Service,云计算,以及大数据的流式计算等等)

比如,我们如下的shell命令:

1
ps auwwx | awk '{print $2}' | sort -n | xargs echo

如果我们抽象成函数式的语言,就像下面这样:

1
xargs(  echo, sort(n, awk('print $2', ps(auwwx)))  )

也可以类似下面这个样子:

1
pids = for_each(result, [ps_auwwx, awk_p2, sort_n, xargs_echo])

好了,让我们来看看函数式编程的Pipeline怎么玩?

我们先来看一个如下的程序,这个程序的process()有三个步骤:

1)找出偶数。
2)乘以3
3)转成字符串返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def process(num):
    # filter out non-evens
    if num % 2 != 0:
        return
    num = num * 3
    num = 'The Number: %s' % num
    return num
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for num in nums:
    print process(num)
# 输出:
# None
# The Number: 6
# None
# The Number: 12
# None
# The Number: 18
# None
# The Number: 24
# None
# The Number: 30

我们可以看到,输出的并不够完美,另外,代码阅读上如果没有注释,你也会比较晕。下面,我们来看看函数式的pipeline(第一种方式)应该怎么写?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def even_filter(nums):
    for num in nums:
        if num % 2 == 0:
            yield num
def multiply_by_three(nums):
    for num in nums:
        yield num * 3
def convert_to_string(nums):
    for num in nums:
        yield 'The Number: %s' % num
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pipeline = convert_to_string(multiply_by_three(even_filter(nums)))
for num in pipeline:
    print num
# 输出:
# The Number: 6
# The Number: 12
# The Number: 18
# The Number: 24
# The Number: 30

我们动用了Python的关键字 yield,这个关键字主要是返回一个Generator,yield 是一个类似 return 的关键字,只是这个函数返回的是个Generator-生成器。所谓生成器的意思是,yield返回的是一个可迭代的对象,并没有真正的执行函数。也就是 说,只有其返回的迭代对象被真正迭代时,yield函数才会正真的运行,运行到yield语句时就会停住,然后等下一次的迭代。(这个是个比较诡异的关键 字)这就是lazy evluation。

好了,根据前面的原则——“使用Map & Reduce,不要使用循环”,那我们用比较纯朴的Map & Reduce吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def even_filter(nums):
    return filter(lambda x: x%2==0, nums)
def multiply_by_three(nums):
    return map(lambda x: x*3, nums)
def convert_to_string(nums):
    return map(lambda x: 'The Number: %s' % x,  nums)
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pipeline = convert_to_string(
               multiply_by_three(
                   even_filter(nums)
               )
            )
for num in pipeline:
    print num

但是他们的代码需要嵌套使用函数,这个有点不爽,如果我们能像下面这个样子就好了(第二种方式)。

1
2
3
pipeline_func(nums, [even_filter,
                     multiply_by_three,
                     convert_to_string])

那么,pipeline_func 实现如下:

1
2
3
4
def pipeline_func(data, fns):
    return reduce(lambda a, x: x(a),
                  fns,
                  data)

好了,在读过这么多的程序后,你可以回头看一下这篇文章的开头对函数式编程的描述,可能你就更有感觉了。

最后,我希望这篇浅显易懂的文章能让你感受到函数式编程的思想,就像OO编程,泛型编程,过程式编程一样,我们不用太纠结是不是我们的程序就是OO,就是functional的,我们重要的品味其中的味道

参考

补充:评论中redraiment这个评论大家也可以读一读。

(全文完)

(转载本站文章请注明作者和出处 酷 壳 – CoolShell.cn ,请勿用于任何商业用途)