-
Notifications
You must be signed in to change notification settings - Fork 34
/
app_lanecn.sql
324 lines (302 loc) · 487 KB
/
app_lanecn.sql
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
-- phpMyAdmin SQL Dump
-- version 3.3.8.1
-- http://www.phpmyadmin.net
--
-- 主机: w.rdc.sae.sina.com.cn:3307
-- 生成日期: 2016 年 05 月 27 日 19:16
-- 服务器版本: 5.6.23
-- PHP 版本: 5.3.3
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
--
-- 数据库: `app_lanecn`
--
-- --------------------------------------------------------
--
-- 表的结构 `admin_menu`
--
CREATE TABLE IF NOT EXISTS `admin_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pid` int(11) NOT NULL COMMENT '父类ID。0是顶级分类',
`name` varchar(50) NOT NULL COMMENT '分类名称',
`in_out` tinyint(4) NOT NULL COMMENT '1是站内链接,2是出站链接',
`url` varchar(100) NOT NULL COMMENT '出站链接地址,in_out为2是生效',
`class` varchar(50) NOT NULL COMMENT '站内链接,类名',
`action` varchar(50) NOT NULL COMMENT '站内链接,方法名',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='后台菜单分类' AUTO_INCREMENT=22 ;
--
-- 转存表中的数据 `admin_menu`
--
INSERT INTO `admin_menu` (`id`, `pid`, `name`, `in_out`, `url`, `class`, `action`) VALUES
(1, 0, '后台分类', 1, '', '', ''),
(2, 1, '添加分类', 1, '', 'adminmenu', 'add'),
(3, 1, '分类列表', 1, '', 'adminmenu', 'lists'),
(4, 0, '文章管理', 1, '', '', ''),
(5, 4, '发表文章', 1, '', 'article', 'add'),
(6, 4, '文章列表', 1, '', 'article', 'lists'),
(7, 0, '管理员管理', 1, '', '', ''),
(11, 10, '添加分类', 1, '', 'menu', 'add'),
(8, 7, '添加管理员', 1, '', 'admin', 'add'),
(9, 7, '管理员列表', 1, '', 'admin', 'lists'),
(10, 0, '前台分类', 1, '', '', ''),
(12, 10, '分类列表', 1, '', 'menu', 'lists'),
(13, 0, '友情链接', 1, '', '', ''),
(14, 13, '添加链接', 1, '', 'friendlink', 'add'),
(15, 13, '链接列表', 1, '', 'friendlink', 'lists'),
(16, 4, '评论管理', 1, '', 'article', 'lists_comment'),
(17, 0, '项目管理', 1, '', '', ''),
(18, 17, '添加手册分类', 1, '', 'itemdocmenu', 'add'),
(19, 17, '手册分类列表', 1, '', 'itemdocmenu', 'lists'),
(20, 17, '添加手册文章', 1, '', 'itemdocarticle', 'add'),
(21, 17, '手册文章列表', 1, '', 'itemdocarticle', 'lists');
-- --------------------------------------------------------
--
-- 表的结构 `admin_user`
--
CREATE TABLE IF NOT EXISTS `admin_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(32) NOT NULL COMMENT '密码',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='管理员表' AUTO_INCREMENT=5 ;
--
-- 转存表中的数据 `admin_user`
--
INSERT INTO `admin_user` (`id`, `username`, `password`) VALUES
(1, 'lixuan', 'bc76daa40e91dbcfada67c7b02b77c08');
-- --------------------------------------------------------
--
-- 表的结构 `info_article`
--
CREATE TABLE IF NOT EXISTS `info_article` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '文章ID',
`mid` int(11) NOT NULL COMMENT '所属分类ID',
`author` varchar(50) NOT NULL COMMENT '作者',
`title` varchar(100) NOT NULL COMMENT '文章标题',
`description` varchar(500) NOT NULL COMMENT '文章描述摘要',
`seo_title` varchar(100) NOT NULL COMMENT 'SEO - title',
`seo_description` varchar(500) NOT NULL COMMENT 'SEO - description',
`seo_keywords` varchar(200) NOT NULL COMMENT 'SEO - 关键词',
`tag` varchar(100) NOT NULL COMMENT '标签',
`clicks` int(11) NOT NULL COMMENT '点击次数',
`content` text NOT NULL COMMENT '文章内容',
`ctime` int(11) NOT NULL COMMENT '创建时间',
`good_num` int(11) NOT NULL COMMENT '被赞的次数',
`bad_num` int(11) NOT NULL COMMENT '被拍砖的次数',
`recommend_type` tinyint(4) NOT NULL COMMENT '推荐类型,1是全站推荐,2是首页推荐',
PRIMARY KEY (`id`),
FULLTEXT KEY `content` (`content`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='文章表' AUTO_INCREMENT=101 ;
--
-- 转存表中的数据 `info_article`
--
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(3, 1, '李轩Lane', 'PHP实现网络刷投票', 'PHP实现网络刷投票,不断变化IP,刷票,刷投票。怒冲排行榜第一名。本文讲解PHP如何实现网络刷投票', 'PHP实现网络刷投票_刷投票_PHP刷票', 'PHP实现网络刷投票,不断变化IP,刷票,刷投票。怒冲排行榜第一名。本文讲解PHP如何实现网络刷投票', 'PHP实现网络刷投票_刷投票_PHP刷票', 'PHP|刷投票', 2044, 'PHP刷投票,让你高居榜首!本文附上刷票方法和防御策略。\r\n案例为一个半月以前。没有及时放出原因有二,一是因为博客域名备案没有下来,没有心情写东西。二是最主要的,及时放出对案例网站有严重的损害,不是我等IT人应有的。\r\nPs:刷票有风险,使用需谨慎。本文谨做学习研究讨论之用,不可用作不正当用途!\r\n\r\n本文为本博客的处女之作,题材源于近日一朋友要求,是因为她的姐姐参加了一个书法比赛,问我能不能在网站上刷投票。作为刚刚出道一年的小菜鸟,我很惶恐。一年前刚刚接触PHP的时候,完全不知道做,现在第一反应就是Curl。\r\n\r\n废话不多说了,直接上代码。\r\n\r\n[code]\r\n<?php\r\nheader(''Content-type: text/html; charset=gb2312'');\r\n//随机生成IP\r\n$ip1 = rand(101, 255).''.'';\r\n$ip2 = rand(1, 255).''.'';\r\n$ip3 = rand(1, 255).''.'';\r\n$ip4 = rand(1, 255);\r\n$ip = $ip1 . $ip2 . $ip3 . $ip4;\r\n$clientIp = ''CLIENT-IP:''.$ip;\r\n$xforwarded = ''X-FORWARDED-FOR:''.$ip;\r\n//设置目标和来源\r\n$url = ''http://www.dunhuangwomen.org.cn/vote/Vote.asp?id=67'';\r\n$referer = ''http://www.dunhuangwomen.org.cn/vote/list.asp?id=2'';\r\n//Curl\r\n$ch = curl_init();\r\ncurl_setopt($ch, CURLOPT_URL, $url); //目标\r\ncurl_setopt($ch, CURLOPT_HTTPHEADER, array($xforwarded, $clientIp)); //构造IP\r\ncurl_setopt($ch, CURLOPT_REFERER, $referer); //来源\r\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);\r\ncurl_setopt($ch, CURLOPT_HEADER, 0);\r\n\r\n$ret = curl_exec($ch);\r\ncurl_close($ch);\r\necho $ret;\r\n?>\r\n[/code]\r\n\r\n好,来分析一下。\r\n1、仿造IP,网站有限制一个IP在一天只可以投一次\r\n2、填写来源,网站会判断请求的来源是否合法路径\r\n其他就是Curl的常规了选项了。\r\n\r\n如何防治?\r\n\r\n本人才疏学浅,仅作跑砖引玉。\r\n\r\n1、限制IP\r\n 本文已经破解\r\n2、限制来源\r\n 本文已经破解\r\n3、验证码。作为最反人类的发明之一,可以使用这个拥有高大上的名字的全自动区分计算机和人类的图灵测试。\r\n 可用Opencv。\r\n4、记录MAC地址。\r\n 理论上每块网卡都有一个唯一的MAC地址,如果更改可能引起冲突而无法上网。目前也可以用软件修改\r\n5、注册会员\r\n 虽说仍然可以突破验证码,Curl填写参数然后POST过去,但是门槛毕竟高了一丁点,还是忽略吧。\r\n6、手机\r\n 投票时输入手机号和短信验证码,成本高,单位不愿意。用户发送某某指令到某某,用户自掏腰包,用户不愿意。\r\n\r\n目前来说,只有验证码,手机并且验证手机的有效性是最好的防治措施。', 1392015111, 33, 18, 0),
(5, 4, '李轩Lane', 'mysql多表随机查询优化方案', 'Mysql随机查询,使用子查询的来在记录中随机取出一条记录,使用MYSQL实现随机查询,多表随机查询和随机排序', 'mysql随机查询_mysql多表随机查询_mysql随机排序', 'Mysql随机查询,使用子查询的来在记录中随机取出一条记录,使用MYSQL实现随机查询,多表随机查询和随机排序', 'mysql随机查询,mysql多表随机查询,mysql随机排序', 'MYSQL|随机查询', 3390, '本文主要谈论如何实现Mysql的随机查询,多表随机查询。在Mysql中随机取出一条记录的实现方法。\r\n我们通常的查询是没有where或者where fields>2这样的方式,这样只能取出在某种条件下的一条或多条,如果条件不变(例如2),那么结果就一直不会有变化。\r\n那么如何实现随机查询呢?本人有两种方法。\r\n\r\n方法一、数据表记录不大的情况下:\r\n[code]\r\nselect * from `table`\r\n[/code]\r\n查出来所有的记录列表,然后array_rand()随机出一个结果的数组的key。连续的key可以使用mt_rand(1, count($list)); 为什么不使用rand而是mt_rand呢?因为mt_rand币rand快4倍。\r\n 这种情况下,查出整个列表,存入到Memcache的缓存或者Redis的NoSQL中,下次直接取出结果集而不需要查表。不过当数据量一旦超过万级别,取出列表就很困难了。\r\n\r\n方法二:使用SQL语句随机\r\nMYSQL函数RAND(),产生一个0-1之间的小数,然后MAX(`id`)可以获得该表中最大的ID。那么MAX(`id`) * RAND()就可以取到表中所有的ID。OK,看语句。\r\n[code]\r\nSELECT * FROM `table` WHERE `id` > (SELECT RAND() * (SELECT MAX(`id`) FROM `table`) LIMIT 0, 1\r\n[/code]\r\n既然MAX(`id`) * MAX(`id`)可以取到表里所有值,那么本语句的WHERE就可以取到本表的所有情况,那么这就是一个所有记录都有可能被取到的随机SQL语句。', 1392889206, 28, 21, 2),
(6, 5, '李轩Lane', 'Ubuntu下安装Linux+Apache+Mysql+PHP', 'Ubuntu下安装Linux+Apache+Mysql+PHP的LAMP四大组建,Ubuntu安装LAMP,Linux安装LAMP的方法,请看本文的。本文以Ubuntu下安装Lamp为例', 'Ubuntu下安装Linux+Apache+Mysql+PHP_Ubuntu安装LAMP_Linux安装LAMP', 'Ubuntu下安装Linux+Apache+Mysql+PHP的LAMP四大组建,Ubuntu安装LAMP,Linux安装LAMP的方法,请看本文的。本文以Ubuntu下安装Lamp为例', 'Ubuntu下安装Linux+Apache+Mysql+PHP,Ubuntu安装LAMP,Linux安装LAMP', 'Linux|LAMP|Ubuntu', 1183, '本文以Ubuntu为例,讲解Linux下如何安装Linux、Apache、Mysql、PHP的LAMP架构。可用于Ubuntu和CentOS系列。\r\n 1、安装Apache,Mysql,PHP,在安装Mysql的时候会要求建立管理员帐号和密码:\r\n[code]\r\nsudo apt-get install php5 apache2 mysql-client mysql-server \r\n[/code] \r\n\r\n2、安装PHP的扩展。如php中的mysql,GD库,CURL等。这样才可以使用GD库做图,mysql扩展,CURL扩展等功能\r\n[code]\r\nsudo apt-get install php5-mysql php5-gd php5-curl \r\n[/code]\r\n\r\n3、修改目录权限,为方便此时测试,暂且修改为777,也就是drwxrwxrwx。在实际中,777可是非常危险的哦。一般apache的项目根目录在/var/www\r\n[code]\r\ncd /var/www\r\nsudo chmod 777 /var/www/\r\n[/code]\r\n\r\n4、安装PHPMyAdmin,安装时会要求选择服务器端软件,选择Apache就好了。还会要求你输入Mysql的帐号和密码。这个帐号和密码是在第一步安装的时候就有提示的。\r\n[code]\r\nsudo apt-get install phpmyadmin\r\n[/code]\r\n\r\n5、为PHPMyAdmin建立软链接,放在/var/www下,这样可以直接通过localhost/phpmyadmin来访问了。\r\n[code]\r\nsudo ln -s /usr/share/phpmyadmin /var/www\r\n[/code]\r\n\r\n重启Apache是/etc/init.d/apache2 restart或者service apache2 restart\r\n\r\nOK,安装好了。自己在/var/www建立index.php吧\r\n[code]\r\ncd /var/www\r\ntouch index.php\r\nvim index.php\r\n<?php \r\necho ''hello wordl'';\r\n?>\r\ntouch phpinfo.php\r\nvim phpinfo.php\r\n<?php\r\nphpinfo();\r\n?>\r\n[/code]\r\n\r\n好了。自行玩吧~', 1392943862, 6, 6, 0),
(7, 1, '李轩Lane', 'PHP 时间种子 批量 随机数', 'PHP时间种子批量随机数。以变化的参数“时间”为种子,批量生成随机数。可以用在激活码,CDK,邀请码,活动期间限量的唯一电子券等情景,先生成十万条来玩玩吧。', 'PHP时间种子批量随机数_PHP如何用时间种子批量生成随机数', 'PHP时间种子批量随机数,本文说明PHP如何用时间种子批量生成随机数。', 'PHP时间种子批量随机数,本文说明PHP如何用时间种子批量生成随机数。', 'PHP|随机数|时间种子', 790, 'PHP时间种子批量随机数。本文说明PHP如何用时间种子批量生成随机数。\r\nPHP函数mt_rand()和rand()会在批量生成的时候是会有几率出现重复的随机数。srand()和mt_srand()在PHP4.1开始已经不在显式调用了,在mt_rand和rand的时候会自动生成种子。因为,在批量随机的时候,我们自己显式条用生成种子,就可以避免重复。为什么呢?因为种子不一样了呀。种子为什么不一样了呢?因为他是时间种子。\r\n[code]\r\n<?php\r\n//存储生存的随机数\r\n$randArr = array();\r\n//生成十万个吧\r\nfor($i=0;$i<100000;$i++){\r\n //生成种子\r\n $date = explode('' '', microtime());\r\n $seed = $date[0];\r\n //种子发生器\r\n mt_srand($seed);\r\n //生成随机数\r\n $randArr[] = mt_rand();\r\n}\r\n?>\r\n[/code]\r\n随机数生成了。并且不会重复的哦。以时间为种子的好处就是省略了在普通的伪随机数会出现重复的情况时进行do{生成随机数code}while(!isset(新生成的一个随机数))的判断步骤。', 1394162554, 73, 20, 0),
(8, 1, '李轩Lane', 'PHP面试题,PHP笔试题(一)', 'PHP面试题、PHP笔试题、PHP基础题和PHP练习题的集合,第一篇。有意思的小题目,来练练手吧。这类题目都是对基础考查。', 'PHP面试题_PHP笔试题_PHP练习题', 'PHP面试题、PHP笔试题和PHP练习题的集合,第一篇。有意思的小题目,来练练手吧', 'PHP面试题,PHP笔试题,PHP练习题', 'PHP|面试题|笔试题', 1945, '题目一:\r\n[code]\r\n<?php\r\necho -10%3;\r\n?>\r\n[/code]\r\n答案:-1。\r\n考查:优先级。\r\n因为-的优先级比%求余的优先级低,也就是-(10%3)。\r\n\r\n题目二:\r\n[code]\r\nprint (int)pow(2,32);\r\n[/code]\r\n答案:0\r\n\r\n题目三:\r\n[code]\r\n//file1.php\r\n<?php\r\n$a = ''123'';\r\n?>\r\n//file2.php\r\n<?php\r\necho include(''file1.php'');\r\n?>\r\n[/code]\r\n答案:1.\r\n考查:返回值。\r\n因include()也是一个函数,有返回值。在成功时返回1,失败时返回错误信息。如果被包含的文件有return,则inculde()成功时返回该文件的返回值。\r\n\r\n题目四:\r\n[code]\r\n<?php\r\n$count = 5;\r\nfunction get_count() {\r\n static $count = 0;\r\n return $count++;\r\n}\r\n++$count;\r\nget_count();\r\necho get_count();\r\n?>\r\n[/code]\r\n答案:1.\r\n考查:static和++。\r\n因static $count,所以只在第一次调用get_count的时候对$count赋值为0,第二次再进来这个函数,则不会第二次赋值。其次就是return $count++和return ++$count了,前者先返回,后者先++再返回。\r\n\r\n题目五:\r\n[code]\r\n<?php\r\n$arr = array(0 =>1,''aa'' => 2,3,4);\r\nforeach($arr as $key => $val){\r\n print($key == ''aa'' ? 5 : $val);\r\n}\r\n?>\r\n[/code]\r\n答案:5534.\r\n考查:类型转换。\r\n因遍历数组第一次的时候,$key和aa的比较实际就是0和aa的比较,一个是int一个是string,这个时候会转换类型,将字符串转换为数字再与数字比较。所以0==''aa''就是0==0,所以为true,也就是输出5。虽然PHP是若类型语言,但是人家也有类型的好吗。\r\n\r\n\r\n题目六:\r\n[code]\r\n<?php\r\necho count (false);\r\n$a = count ("567") + count(null) + count(false);\r\necho $a;\r\n?>\r\n[/code]\r\n答案:2.\r\n考查:count的用法。\r\n因count()的官方解释“If the parameter is not an array or not an object with implemented Countable interface, 1 will be returned.”.意思是说,如果不是数组或者对象的其他类型,返回1.那么这个值应该就是1+0+1了(boolen人家也是一个类型,虽然是讨厌的false)。NULL的意思是没有值,难道在计数函数中还能有1?\r\n\r\n题目七:\r\n[code]\r\n<?php\r\n$arr = array(1,2,3);\r\nforeach($arr as &$val) {\r\n $val += $val % 2 ? $val++ : $val--;\r\n}\r\n$val = 0;\r\nprint(join('''',$arr));\r\n?>\r\n[/code]\r\n答案:330。\r\n考查:++和&。\r\n因foreach结束后的数组应该是array(3,3,7);最后给第三个元素赋值为0,所以就是330了。其中注意的是&,如果有&则是对原变量操作,如果没有,则是先生成一个新变量,然后给这个变量复制,最后操作的是这个新变量。\r\n\r\n题目八:\r\n[code]\r\n<?php\r\necho intval((0.1+0.7)*10);\r\n?>\r\n[/code]\r\n答案:7。\r\n考查:浮点数的概念。\r\n因0.1+0.7=0.8 0.8*10=8 所以转换成整数后还是8?错!因为0.1+0.7=0.8是浮点数,0.8*10在数学计算中是正整数8,可是在计算机中它仍然是浮点数8,什么叫浮点数8?每一个看起来像整数的浮点数,其实都不是整数,比如这个8,它其实只是7.9999循环,无限接近于8,转换成整数会舍弃小数部分,就是7喽。\r\n\r\n题目九:\r\n[code]\r\n<?php\r\nini_set(''display_errors'',0);\r\n$arr = array(1=>1,3=>3);\r\n$i = 2;\r\n$a = ''test'' . isset($arr[$i]) ? $arr[$i] : $i;\r\n请问$a的值是什么?\r\nA、test B、NULL C、2 D、test2\r\n?>\r\n[/code]\r\n答案:B。\r\n考查:优先级。\r\n因“."的优先级高于三元运算符"?:"。所以程序其实报错了。会说$arr的索引2不存在。\r\n\r\n题目十:\r\n[code]\r\n<?php\r\n$a = 3;\r\n$b = 5;\r\nif($a = 5 || $b = 7) {\r\n $a++;\r\n $b++;\r\n}\r\necho $a . " " . $b;\r\n?>\r\nA、6 8 B、6 6 C、2 6 D、1 6 E、4 6\r\n[/code]\r\n答案:D。\r\n考查:优先级,基础概念,++。\r\n因“="的优先级低于“||”,所以先逻辑判断再赋值。也就是($a = (5 || $b = 7))。所以,最后其实给a赋值了,$a等于1.\r\n\r\n题目十一:\r\n[code]\r\n<?php\r\n$x = 2;\r\necho $x == 2 ? ''我'' : $x == 1 ? ''你'' : ''它'';\r\n?>\r\n输出的结果是()\r\nA、我\r\nB、你\r\nC、它\r\nD、syntax error\r\n[/code]\r\n答案:B。\r\n考查:优先级。\r\n因“=="的优先级高于“?:”。我已经不想说为什么了,整理一下这种东西,我感觉要疯了,除了笔试题外估计也不会遇到了吧。', 1394175615, 33, 21, 0),
(9, 5, '李轩Lane', 'Fedora20安装VirtualBox_Fedora20_VirtualBox', 'Fedora20安装VirtualBox虚拟机的方法。其实是非常狗血的一个剧情。你们肯定想不到我是如何解决的。安装时大范围的报错,被告知缺少库文件。', 'Fedora20安装VirtualBox_Fedora20安装VBOX_Fedora20安装虚拟机', '本文讲解Fedora20安装VirtualBox,Fedora20安装VBOX和Fedora20安装虚拟机的方法。其实是非常狗血的一个剧情。你们肯定想不到我是如何解决的', 'Fedora20安装VirtualBox,Fedora20安装VBOX,Fedora20安装虚拟机', 'Fedora|VirtualBox|vbox', 1148, '<p>由于我的Ubuntu实在用这不爽,作为Linux新手我完全不能应对Ubuntu的各种报错,比如今天图形界面的关机按钮没有了,明天图形界面的时间没有了,后天图形界面的输入法不显示了。之前用过几天的Fedora,感觉界面比较炫。\r\n在Fedora 20 安装VirtualBox的时候发生了些小意外。\r\n第一步、下载Vbox。上官网,下对应自己系统的版本。\r\n[code]\r\nsudo rpm -i VirtualBox-4.3-4.3.8_92456_fedora18-1.i686.rpm\r\n[/code]\r\n然后。让我头疼的事情来了。\r\n[code]\r\n警告:VirtualBox-4.3-4.3.8_92456_fedora18-1.i686.rpm: 头V4 DSA/SHA1 Signature, 密钥 ID 98ab5139: NOKEY\r\n错误:依赖检测失败:\r\n libGL.so.1 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libQtCore.so.4 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libQtGui.so.4 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libQtNetwork.so.4 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libQtOpenGL.so.4 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libSDL-1.2.so.0 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libX11.so.6 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libXcursor.so.1 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libXext.so.6 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libXinerama.so.1 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libXmu.so.6 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libXt.so.6 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libasound.so.2 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.1) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.1.1) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.1.2) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.1.3) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.15) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.2) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.2.3) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.3) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.3.2) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libc.so.6(GLIBC_2.4) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libcrypt.so.1 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libcrypt.so.1(GLIBC_2.0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libcrypto.so.10 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libcrypto.so.10(libcrypto.so.10) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libcurl.so.4 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libdevmapper.so.1.02 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libdevmapper.so.1.02(Base) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libdl.so.2 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libdl.so.2(GLIBC_2.0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libdl.so.2(GLIBC_2.1) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libgcc_s.so.1 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libgcc_s.so.1(GCC_3.0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libgcc_s.so.1(GLIBC_2.0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libm.so.6 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libm.so.6(GLIBC_2.0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libm.so.6(GLIBC_2.1) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpng15.so.15 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpng15.so.15(PNG15_0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpthread.so.0 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpthread.so.0(GLIBC_2.0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpthread.so.0(GLIBC_2.1) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpthread.so.0(GLIBC_2.2) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpthread.so.0(GLIBC_2.3.2) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpthread.so.0(GLIBC_2.3.3) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpthread.so.0(GLIBC_2.3.4) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libpython2.7.so.1.0 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n librt.so.1 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n librt.so.1(GLIBC_2.2) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libssl.so.10 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libssl.so.10(libssl.so.10) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libstdc++.so.6 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libstdc++.so.6(CXXABI_1.3) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libstdc++.so.6(CXXABI_1.3.1) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libstdc++.so.6(GLIBCXX_3.4) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libstdc++.so.6(GLIBCXX_3.4.11) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libstdc++.so.6(GLIBCXX_3.4.15) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libstdc++.so.6(GLIBCXX_3.4.9) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libvpx.so.1 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libxml2.so.2 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libxml2.so.2(LIBXML2_2.4.30) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libxml2.so.2(LIBXML2_2.6.0) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libxml2.so.2(LIBXML2_2.6.8) 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n libz.so.1 被 VirtualBox-4.3-4.3.8_92456_fedora18-1.i686 需要\r\n[/code]\r\n我去年买了个表啊!!!这么多的库文件却是难道要我一个一个手打安装????\r\n各种Baidu\\Google。然后。被我发现了一个好发方法。。\r\n双击打开!!\r\n然后他会自己安装这些关联关系的软件包。\r\n\r\n是不是要喷?开头我就说了很狗血的~\r\n\r\n-------------我是分割线------------------------\r\n\r\n安装完成,打开。报错\r\n不能为虚拟电脑 XP 打开一个新任务.\r\nThe virtual machine 'XP' has terminated unexpectedly during startup with exit code 1.\r\n\r\n\r\n\r\nRTR3InitEx failed with rc=-1912 (rc=-1912)\r\n\r\nThe VirtualBox kernel modules do not match this version of VirtualBox. The installation of VirtualBox was apparently not successful. Executing\r\n\r\n'/etc/init.d/vboxdrv setup'\r\n\r\nmay correct this. Make sure that you do not mix the OSE version and the PUEL version of VirtualBox.\r\n\r\n按照提示输入/etc/init.d/vboxdrv setup都提示OK了也没有用啊。\r\n搜索的解决方法\r\n[code]\r\nsudo yum install kernel-devel\r\nsudo yum install gcc\r\nsudo /etc/init.d/vboxdrv setup\r\n[/code]\r\n再次打开,还是不可以,报同样的错。\r\n\r\n继续搜索~\r\n来了个狠的,更新系统\r\n[code]\r\nsudo yum -y update\r\nsudo /etc/init.d/vboxdrv setup\r\n[/code]\r\nStopping VirtualBox kernel modules [ OK ]\r\nUninstalling old VirtualBox DKMS kernel modules [ OK ]\r\nRemoving old VirtualBox kernel module [ OK ]\r\nTrying to register the VirtualBox kernel modules using DKMS[ OK ]\r\nStarting VirtualBox kernel modules [ OK ]\r\n好了。可以了哦</p>', 1394413713, 10, 14, 0),
(10, 7, '李轩Lane', 'Fedora20_VirtualBox4.3虚拟机安装windows XP', 'Fedora20下VirtualBox4.3虚拟机安装windows XP,遇到的问题以及如何解决的。主要是BIOS的信息读取错误时如何安装。', 'Fedora20下VirtualBox4.3虚拟机安装windows XP_vbox安装xp_virtualbox安装xp', 'Fedora20下VirtualBox4.3虚拟机安装windows XP,遇到的问题以及如何解决的。', 'Fedora20下VirtualBox4.3虚拟机安装windows XP,vbox安装xp,virtualbox安装xp', 'VirtualBox|XP|虚拟机', 962, '本文前提是安装好Virtual4.3。在Fedora下如何安装请点击http://www.lanecn.com/article/main/aid-9查看。 \r\n打开软件,点击新建。输入一个名字,随便写,自己能认就OK。按照系统默认的选项一直下一步。一直创建完成。选中创建的项目后点击上方的显示。选择一个ISO镜像文件。(XP,WIN7,老毛桃,深度,雨林木风等)。\r\n现在虚拟机就开始开机了。\r\n安装和正常是一样的。\r\n本文主要说遇到如下错误:\r\nUIDE,01-15-2008 80-MB cache,CD/DVD name is mscd001\r\nIDE1 controller at IO address Fgooh ,chip I.D.1002438ch\r\nIDE2 controller at IO address Fbooh ,chip I.D.10024380h\r\nIDE2 secondary-slave disk is WDC WD 1600AAjs-22psao,ata-133\r\nCD:IDE1 primary-master, teclast DHB16H,ATA-33.\r\n\r\n解决方案:\r\n1、对虚拟磁盘分区。\r\n2、进入winPE。\r\n3、GHOST。\r\n4、一般下载的IOS镜像里有GHO文件。可以直接用来GHOST.\r\n从PE安装就不会有上面的提示。', 1394428722, 5, 5, 0),
(11, 1, '李轩Lane', '本博客PHP源码下载,基于自己开发的超轻量级的PHP框架', '本博客源码下载,基于自己开发的超轻量级的框架。PHP博客源码下载。对SEO超级友好。', '博客源码_博客源码下载_PHP博客源码下载', '本博客源码下载,基于自己开发的超轻量级的框架。博客源码下载。PHP博客源码下载。对SEO超级友好。', '博客源码,博客源码下载,PHP博客源码下载', 'PHP|博客|框架', 14583, '版本1.2.0改动日志:\r\n 1、前端框架BootStrap2.0升级为3.0.3。\r\n 2、布局由全屏显示变更为居中显示。\r\n 3、将评论用户提交的URL不转换为链接。\r\n 4、将错误页面统一为BootStrap布局\r\n 5、删除多个CSS和JS,提升加载速度。\r\n 6、界面美化\r\n 7、添加TAG随即显示\r\n\r\n版本1.2.0:发布日期:2014-05-27 下载地址:https://github.com/lixuancn/LX_Blog/tree/master\r\n\r\n版本1.0.0:发布日期:2014-03-17 下载地址:http://www.lanecn.com/themes/download/blog_1.0.0.rar\r\n\r\n环境:PHP+Mysql\r\n\r\n前端:bootstrap\r\n\r\n说明:解压后是源码文件夹和数据库的MySQL文件。\r\n\r\n数据库配置信息请在config/develop/cloud.conf.php和config/online/cloud.conf.php修改。\r\n\r\nURL配置请在config/develop/sys.conf.php和config/online/sys.conf.php\r\n\r\n使用请保留鄙人的友情链接。谢谢~\r\n\r\n欢迎指正BUG。\r\n\r\n因时间关系,做的比较粗糙。细节尚未完善。\r\n\r\n可直接跟贴回复。每天都会看的。\r\n\r\n问题请直接在下方留言,邮件和论坛看的不及时。留言请注明邮件。谢谢~', 1395038461, 114, 34, 1),
(12, 4, '李轩Lane', 'MySql计数器,如网站点击数,如何实现高性能高并发的计数器功能', 'Mysql计数器功能,单一的字段在高并发下的工作并不理想,本文分享鄙人的浅见。实现高性能Mysql。在高并发下良好的工作。', 'Mysql计数器_高性能Mysql计数器_Mysql实现计数器', 'MySql计数器,如网站点击数,浏览量等,如何用Mysql实现高性能计数器,在高并发下良好的工作。', 'Mysql计数器,高性能Mysql计数器,Mysql实现计数器', 'Mysql|计数器|高性能', 6109, '现在有很多的项目,对计数器的实现甚是随意,比如在实现网站文章点击数的时候,是这么设计数据表的,如:”article_id, menu_id, article_name, article_content, article_author, article_view......在article_view中记录该文章的浏览量。诈一看似乎没有问题。对于小站,比如本博客,就是这么做的,因为小菜的博客难道会涉及并发问题吗?答案显而易见,一天没多少IP,而且以后不会很大。\r\n 言归正传,对文章资讯类为主的项目,在浏览一个页面的时候不但要进行大量的查(查询上文的记录,已经所属分类的名字、热门文章资讯评论、TAG等),还要进行写操作(更新浏览数点击数)。把文章的详细内容和计数器放在一张表尽管对开发很方便,但是会造成数据库的压力过大(不然为什么大项目都要分库分表呢)。\r\n 那么,分两张表存放就好了么?一张表存文章详细信息,另一张表单独存计数器。\r\n[code]\r\nCREATE TABLE `article_view`(\r\n `article_id` int(11) NOT NULL,\r\n `view` int(11) NOT NULL,\r\n PRIMARY KEY (`article_id`)\r\n)ENGINE=InnoDB;\r\n[/code]\r\n 这种方式,虽然分担了文章表的压力,但是每当有一个进程请求更新的时候,都会产生全局的互斥锁,只能串行,不能并行。在高并发下会有较长的等待时间。\r\n 另一种比较好的办法是对每一个文章的计数器不是一行,而是多行,比如吧,一百行。每次随机更新其中一行,该文章的浏览数就是所有行的和。\r\n[code]\r\nCREATE TABLE `article_view`(\r\n `article_id` int(11) NOT NULL,\r\n `pond` tinyint(4) NOT NULL COMMENT ''池子,就是用来随机用的'',\r\n `view` int(11) NOT NULL,\r\n PRIMARY KEY (`article_id`, `pond`)\r\n)ENGINE=InnoDB;\r\n[/code]\r\n 小访问量的随机池子100个肯定多了,三五个足矣。每次访问的时候,随机一个数字(1-100)作为pond,如何该pond存在则更新view+1,否则插入,view=1。借助DUPLICATE KEY,不然在程序里是实现得先SELECT,判断一下再INSERT或者UPDATE。\r\n[code]\r\nINSERT INTO `article_view` (`article_id`, `pond`, `view`) VALUES (`123`, RAND()*100, 1) ON DUPLICATE KEY UPDATE `view`=`view`+1\r\n[/code]\r\n 获取指定文章的总访问量的时候:\r\n[code]\r\nSELECT SUM(`view`) FROM `article_view` WHERE `article_id`=''123''\r\n[/code]\r\n\r\nPs:凡事都是双刃剑。为了更快的读我们通常要牺牲一些东西。在读比较多的表要加快读的速度,在写较多的表要加快写的速度。各自权衡。在加快读的速度的时候,我们牺牲的并不仅仅是写的性能,还有开发成本,开发变的更复杂,维护成本等。所以并不是读的速度越快越好,需要找一个平衡点。\r\n\r\n注:这里仅仅是Mysql方面,有人会说高并发下你这是直接读写Mysql啦,项目的瓶颈本来就在数据库啦。。。其实。。。这里只是说Mysql的表怎么去设计而已。你完全可以在这个地方用队列去写表,你也可以把计数器在内存中保存,一直来累加,1个小时持久化一次。你也可以去用号称每秒读写十万次的Redis。', 1396107042, 53, 28, 0),
(14, 6, 'default7', '【转】恶性循环:舍不得投资,得不到回报', '文章转载自CSDN。原标题是【聊一聊】程序员的恶性循环 ! 正好解释我现在的一些困惑。舍不得投资,就没有回报!等着得到回报后在投资,是愚蠢的!', '恶性循环_舍不得投资_得不到回报', '恶性循环,舍不得投资,得不到回报', '恶性循环,舍不得投资,得不到回报', '投资|回报', 1054, '原文地址:http://bbs.csdn.net/topics/390729660\r\n\r\n穷人的恶性循环:\r\n穷 -> 需要努力工作 -> 没有时间去交际 -> 人脉越来越狭窄 -> 工作越来越难做 -> 越需要努力去工作 -> 越没有时间去发展人脉 -> 越穷\r\n\r\n富人的良性循环:\r\n有钱 -> 工作很轻松 -> 很多时间都在交际上 -> 人脉越来越广 -> 工作越来越不用努力 -> 越有更多的时间精力去发展人脉 -> 越富有 \r\n\r\n程序员的恶性循环:\r\n加班 -> 没空学习 -> 老是写同等水平代码 -> 无法提升代码质量 -> 老是出BUG -> 老是需要修改 -> 加班 -> ....\r\n\r\n\r\n\r\n想到个事情,IP5都出来的时候,我还是在用那种只能打电话接电话的直板手机,每次公司聚会的时候,老总给每个人发邮件,大家都拿出触屏的来收邮件,唯独自己一个人还是那种最老的手机 —— 三星E110C,当时自己真恨不得找个地洞钻下去,完全来错了地方一样。上司都说你每个月工资也五六K了,怎么不换个好一点的手机?穷惯了,舍不得,所受的教育一定要节俭,思想斗争,还是坚持节俭。舍不得花三四K买个好的手机。。。从小穷惯了节俭惯了,思想迂腐,只知道省钱不知道投资。\r\n\r\n\r\n还想到一个事情,我在广州天河太古汇那上班,中午吃饭,每次都不敢进那种装修好一点的餐馆吃饭。总觉得那种地方贵吧,具体有多贵自己也说不出来也不知道,反正就是一想到就觉得贵,舍不得心疼钱。然后我每次中午要跑很远去离工作地点很远的石牌城中村吃午餐,十多二十几块钱的一份盒饭,又不卫生人有超级多,但是自己一直忍着,没办法没钱,穷命穷受罪。有一次忙一个东西实在是太远,一狠心就在上班的楼下那些餐馆吃饭吧,结果一看菜单,才发现哇靠原来这么便宜,10元一份的比比皆是,而且还有座位,环境比起城中村的那些没座位还脏兮兮的好不知道多少倍。突然之间我似乎明白出一些道理。\r\n\r\n\r\n第三件事,我以前总是没有鞋子穿,不信可以看我以前在论坛水区发的贴,提问什么鞋子耐穿。那时候我每次都是找那种15元 25元一双的“亏本甩卖”的鞋店去买鞋,里面都是15 25一双,但是我总会挑选50、99一双的,为的是希望可以穿得久一点,不过很遗憾,每次都是最多2个月就破了报废了。然后每天都是没鞋子穿,每天都是穿着破鞋去上班,而屋里总是一大堆鞋子,但是都破了,每隔一两个月就要去这样的店铺买鞋子。后来偶然一次我算了一下,每月几乎要买一双鞋子,花费50到99,3个月就是150,还不如买一双好一点的名牌鞋子试试。但由于一直穿的鞋子不管是25 还是 50 还是99都是不到2个月就坏了,所以更是不敢去买几百一双的鞋子。恶性循环!最后一次铤而走险,花了几百块去专卖店买了一双某牌子的鞋子(这里还是隐藏牌号,免得广告)。发现居然穿了3个月都没坏掉现在还一直穿着很好。从此之后我再也不进那种25元一双的鞋店买鞋子\r\n\r\n\r\n第四件事,我用的第一部智能手机是HTC的,G13。当时在车上、外面看到每个人用的都是HTC,认为HTC应该是非常不错的吧,ZOL手机上都拍第二了,很牛逼吧!于是花了将近2000块在国美买了一国行HTC。不过用了几个月就越来越卡,越来越慢,512M内存。一年保修期之后 刷机了,删除自带的软件了还是就只能打电话接电话了,根本算不上智能机了。之后对只能手机产生了严重的怀疑,科技这么发达,怎么一个排名第二的智能手机这么差,不说运行游戏就连QQ都运行不了了!最后想过换三星的手机,因为都是安卓的,担心又会像HTC这样,完全就只能打电话发短信。咬牙买苹果。其实我很高心自己当时能做这样的决定,用了才发现对比之下HTC根本就不能算智能手机!苹果512M内存都可以安装无数个软件应用,而 HTC G13安装了QQ QQ空间 QQ同步助手 UC浏览器 搜狗输入法就什么都装不了,且一运行就黑屏\r\n\r\n\r\n第五件事,我在广州一家网络公司做网站程序员,月薪4K5,是我在武汉2K工资的2倍还多,心里非常哈皮,所以工作非常努力卖命。公司就我一个PHP程序员,一开始不怎么加班,但到最后我却弄得每天都加班,,,,撑了2年我最后还是累的主动辞职了,,,出来之后才发现这公司给的工资比行情低至少2K,,,,但我2年间根本从没去打探过行情,也没时间精力去打探,,,\r\n\r\n\r\n\r\n\r\n几个故事之间似乎蕴涵着一定的道理', 1397179442, 15, 6, 0),
(15, 8, '李轩Lane', '面向对象的洗礼:设计模式(一)之简单工厂模式', '设计模式,是面向对象的洗礼,面向对象的思维体操,常见的共28种设计模式,本篇谈谈设计模式中最常见的一种,那就是简单工厂模式。抽象,封装,对不同的需求进行分发,一个需求的改动不需要改变其他,低耦合,高重用。', '简单工厂模式_设计模式_设计模式之简单工厂模式', '面向对象的思维体操,设计模式中最常见的一种,简单工厂模式。以PHP的视角,来演练设计模式之简单工厂模式', '简单工厂模式,设计模式,设计模式之简单工厂模式', 'PHP|设计模式|简单工厂模式', 712, '昨晚开始看设计模式,我决定没看一种,就把它记录下来。一是晚上看,早上到公司,边写边回味。二是决定每看一章就写一篇博客,可以监督自己不会看着看着半途而废。\r\n 这应该就是一个系列博客了,书目录总共28种设计模式。这本书是我去赶集面试时推荐给我的,推荐了2本,一本大话设计模式,一本大话数据结构。想来想去,明白了一点,语言只是工具,真正的核心在于算法,设计模式,数据结构。本系列将已PHP为代码实现\r\n 设计模式是对OOP的思维体操,本篇是设计模式之简单工厂模式。\r\n 场景:实现PHP连接Mysql。\r\n[code]\r\n<?php\r\n$conn = mysql_connect(''localhost'', ''root'', '''');\r\nmysql_select_db(''blog'', $conn);\r\n?>\r\n[/code]\r\n 就这个?搞笑呢?项目里难道也用面向过程的?\r\n[code]\r\n<?php\r\nclass MysqlDb{\r\n private $conn = '''';\r\n public function connect($host, $username, $password){\r\n if(empty($conn)){\r\n $this->conn = mysql_connect($host, $username, $password);\r\n }\r\n }\r\n\r\n public function selectDb($dbName){\r\n mysql_select_db($dbName, $this->conn);\r\n }\r\n}\r\n?>\r\n[/code]\r\n 现在,请给我加一个查询方法\r\n[code]\r\n<?php\r\nclass MysqlDb{\r\n private $conn = '''';\r\n public function connect($host, $username, $password){\r\n if(empty($conn)){\r\n $this->conn = mysql_connect($host, $username, $password);\r\n }\r\n }\r\n\r\n public function selectDb($dbName){\r\n mysql_select_db($dbName, $this->conn);\r\n }\r\n\r\n public function query($sql){\r\n return mysql_query($sql);\r\n }\r\n \r\n public function selectOne($id){\r\n $sql = "SELECT * FROM `tableName` WHERE `id` = ''".$id."'' LIMIT 0, 1";\r\n return $this->query($sql);\r\n }\r\n\r\n public function selectList($id = ''''){\r\n if(!empty($id)){\r\n $where = "WHERE `id` = ''".$id."''";\r\n }\r\n $sql = "SELECT * FROM `tableName`".$where;\r\n return $this->query($sql);\r\n }\r\n}\r\n?>\r\n[/code]\r\n 好,现在项目发展了,单单Mysql不能满足需求了,请给我添加一个Redis。\r\n[code]\r\n<?php\r\nclass RedisDb{\r\n private $conn = '''';\r\n public function connect($host, $username, $password){\r\n if(empty($conn)){\r\n $this->conn = new Redis();\r\n $this->conn->connect($host, $port);\r\n $this->conn->auth($password);\r\n $this->conn->select($dbName);\r\n }\r\n }\r\n \r\n public function getValue($key){\r\n return $this->conn->get($key);\r\n }\r\n\r\n public function setValue($key, $value){\r\n return $this->conn->set($key, $value);\r\n }\r\n}\r\n?>\r\n[/code]\r\n 好了,难道每次都要在代码里调用这2个类?当然不!\r\n[code]\r\n<?php\r\n/**\r\n * 数据库工厂类 - 这就是简单工厂模式的分发。调用上面的几个类\r\n */\r\nclass DbFactory{\r\n private static $dbObj = '''';\r\n public static function init($dbType){\r\n if(empty(self::$dbObj)){\r\n self::$dbObj = self::dbSwitch($dbType);\r\n }\r\n return $dbObj;\r\n }\r\n \r\n private static function dbSwitch($dbType){\r\n $dbType = strtolower($dbType);\r\n $obj = '''';\r\n switch($dbType){\r\n case ''mysql'':\r\n $obj = new MysqlDb();\r\n break;\r\n case ''redis'':\r\n $obj = new RedisDb();\r\n break;\r\n case ''mysqli'':\r\n $obj = new MysqliDb();\r\n break;\r\n case ''pdo'':\r\n $obj = new PdoDb();\r\n break;\r\n default :\r\n exit(''非法操作'');\r\n }\r\n return $obj;\r\n }\r\n}\r\n?>\r\n[/code]', 1397522886, 7, 6, 0),
(16, 8, '李轩Lane', '面向对象的洗礼:设计模式(二)之策略模式', '策略模式,策略就是算法和变化,策略模式就是对算法和变化的封装。是条件选择从客户端到服务端的转移。客户端与算法类的彻底隔离。以PHP代码实现', '策略模式_设计模式_设计模式之策略模式(算法的封装))', 'PHP策略模式,策略就是算法和变化,策略模式就是对算法和变化的封装。是条件选择从客户端到服务端的转移。客户端与算法类的彻底隔离', 'PHP策略模式,设计模式,设计模式之策略模式(算法的封装))', 'PHP|设计模式|策略模式', 613, '策略模式,策略就是算法和变化,策略模式就是对算法和变化的封装。是条件选择从客户端到服务端的转移。客户端与算法类的彻底隔离。\r\n[code]\r\n<?php\r\nabstract class Strategy{\r\n public $paramA = '''';\r\n public $paramB = '''';\r\n public function getResult(){\r\n\r\n }\r\n}\r\nclass AlgorithmA extends Strategy{\r\n public function algorithmA(){\r\n //算法A的实现\r\n }\r\n}\r\nclass AlgorithmB extends Strategy{\r\n public function algorithmB(){\r\n //算法B的实现\r\n }\r\n}\r\nclass AlgorithmC extends Strategy{\r\n public function algorithmC(){\r\n //算法C的实现\r\n }\r\n}\r\n?>\r\n[/code]\r\n场景: 沃尔玛要做一个收银软件。有打8折,打5折等,有每满100减20等。\r\n[code]\r\n<?php\r\n//抽象类\r\nabstract class Pay{\r\n public $cash = '''';\r\n public $total = '''';\r\n public function getResult(){\r\n return $this->total;\r\n }\r\n}\r\n//打折\r\nclass Discount extends Pay{\r\n public function algorithm($cash, $discount=0.8){\r\n $this->total = $cash * $discount;\r\n return $this->getResult();\r\n }\r\n}\r\n//满多少减多少\r\nclass Reduce extends Pay{\r\n public function algorithm($cash, $satisfied=100, $returnCash=20){\r\n $this->total = $cash - floor($cash / $satisfied) * $returnCash;\r\n return $this->getResult();\r\n }\r\n}\r\nclass Context{\r\n private $obj;\r\n public function __construct($type){\r\n switch($type){\r\n case 1:\r\n $this->obj = new Discount();\r\n break;\r\n case 2:\r\n $this->obj = new Reduce();\r\n break;\r\n }\r\n }\r\n public function algorithm(){\r\n $this->obj->algorithm();\r\n }\r\n}\r\n//客户端\r\n$obj = new Context($_GET[''type'']);\r\necho $obj->algorithm();\r\n?>\r\n[/code]\r\n优点:客户端不需要做条件判断,而且仅仅需要认识一个类即可。乍一看和简单工厂很相似呢。', 1397610188, 10, 11, 0),
(17, 8, '李轩Lane', '面向对象的洗礼:设计模式(三)之单一原则,避免万能类', '面向对象的软件开发中,有一个基本原则,那就是单一原则,是设计模式的重点。单一原则,功能单一的类,避免万能类。如果一个类的空能多于一点,就应该拆分成2个类。是面向对象的设计模式中最重要的一个原则。', '单一原则_设计模式_设计模式之单一原则_避免万能类', '面向对象的软件开发中,有一个基本原则,那就是单一原则,是设计模式的重点。单一原则,功能单一的类,避免万能类', '单一原则,设计模式,设计模式之单一原则,避免万能类', 'PHP|设计模式|单一原则', 497, '面向对象的软件开发中,有一个基本原则,那就是单一原则,是设计模式的重点。单一原则,功能单一的类,避免万能类。如果一个类的空能多于一点,就应该拆分成2个类。是面向对象的设计模式中最重要的一个原则。\r\n 举个例子,在智能手机刚刚出现的时候,诺基亚占据世界大半壁江山。智能手机可以打电话,发短信,浏览网页,玩游戏,拍照,录像等等,但是,拿拍照来说,拍照比不过傻瓜相机(如今也比不过单反)。尽管将大量的功能融合为一台设备,携带和充电更方便,但是效果并不如单一功能的强大。这就引入了“单一原则”。\r\n 在软件开发过程中,单一原则是设计模式中非常重要的思想。如果,你能够在一个类中找到多于一个的功能,那么,这个类就该进行抽象和拆分了。在OOP中有一个大忌讳,就是万能类。一个成千上万行的类,臃肿而庞大,为什么不柴分成多个类呢?每个类负责一个功能,各思其职。', 1397697496, 5, 5, 0),
(18, 8, '李轩Lane', '面向对象的洗礼:设计模式(四)之开放-封闭原则', '开放-封闭原则,是面向对象的核心思想,使用开放-封闭原则的设计模式,可以获得那些声称使用面向对象可以获得的巨大好处,即可扩展性,易维护性,高复用性,超灵活性。', '开放-封闭原则_设计模式_设计模式之开放-封闭原则', '开放-封闭原则,是面向对象的核心思想,使用开放-封闭原则的设计模式,可以获得那些声称使用面向对象可以获得的巨大好处。', '开放-封闭原则,设计模式,设计模式之开放-封闭原则', 'PHP|设计模式|开放-封闭原则', 539, '开放-封闭原则,是面向对象的核心思想,使用开放-封闭原则的设计模式,可以获得那些声称使用面向对象可以获得的巨大好处,即可扩展性,易维护性,高复用性,超灵活性。\r\n 开放原则:对扩展时开放的!\r\n 封闭原则:对修改时关闭的!\r\n 就是说,一个良好的类,欢迎其他的类去继承它,使用它。但是,不欢迎对它进行修改。如果要修改,以便实现新功能,那么,不如去新开发一个类。当然,绝对的不修改是不可能的。这就要求在开发中多思考,多考虑将来有可能面对的修改,降低对某个特定功能的耦合度。\r\n 请注意,开放-封闭原则在OOP中的地位,是核心思想!\r\n 扩展性:容易新增多个软件包;\r\n 维护性:维护时只需要修改一个类中的一个函数即可,完全不会涉及到其他的代码;\r\n 复用性:随时随地,拿来就用;\r\n 灵活性:因为可以扩展,容易维护,可以复用,所以灵活。\r\n 举例:一台电脑,内存条坏了只需要拔下内存条即可,显卡需要升级只需要拔下旧显卡,插上新显卡。CPU风扇坏了只需要更换风扇而不需要更换CPU。无论是Inter还是AMD,每一小块芯片都有许多的复杂的指令集,我们不需要知道。内存条厂商也不需要知道CPU和主板的指令集,将内存条根据针脚插入主板中,就可以工作,因为它依靠针脚(接口)来传输数据。各个硬件之间相互独立。对某个硬件而言,对内我的指令集和工作方式是封闭的,你不可以修改也不需要知道,对外,我有接口,支持扩展,大家可以把我插了就用。这就是开放-封闭原则的体现。是高聚能低耦合的典型例子。', 1397698194, 4, 5, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(19, 8, '李轩Lane', '面向对象的洗礼:设计模式(五)之依赖倒转原则', '依赖倒转原则,是面向对象的标识,以里氏代换原则为基础,使的开放-封闭原则的实现成为了可能。针对接口的而不是针对实现编程。', '依赖倒转原则_设计模式_设计模式之依赖倒转', '依赖倒转原则,是面向对象的标识,以里氏代换原则为基础,使的开放-封闭原则的实现成为了可能。', '依赖倒转原则,是面向对象的标识,以里氏代换原则为基础,使的开放-封闭原则的实现成为了可能。针对接口的而不是针对实现编程。', 'PHP|设计模式|依赖倒转原则', 676, '依赖倒转原则,是面向对象的标识,以里氏代换原则为基础,使的开放-封闭原则的实现成为了可能。针对接口的而不是针对实现编程。\r\n 场景:高内聚低耦合的计算机主机,上篇中提到过的例子http://www.lanecn.com/article/main/aid-18。内存坏了可以直接拔掉换一个新的,不会说华硕的主板就不能插你刚从戴尔的主板上拔下的内存。所以,内存条的设计是针对接口的,是一个统一的标准接口,不是一个主板厂商提供一个接口方式。再所以一下,内存条的设计不是为了实现而去设计的,如果是为了实现,那么我现在要实现它插在戴尔主板上的内存条,它在华硕主板就不能用了。由此,引出一个原则“依赖倒转原则”。\r\n 依赖倒转原则:针对接口编程,而不是针对实现编程。高层模块不能依赖于底层模块,而是两者都共同依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。\r\n 新手总是面向过程的开发,把常用的函数都写成底层的函数。比如数据库操作函数。客户端直接调用数据库操作函数。那么假如有一天客户要求更改数据库呢?就要修改底层的数据库操作函数,但是,<a href="http://www.lanecn.com/article/main/aid-18">面向对象的洗礼:设计模式(四)之开放-封闭原则</a>,对修改是封闭的,不应该用修改的方式,而是用扩展的方式,把相同的操作函数都抽象出来。所以是针对接口,而不是针对实现。另一个原则为针对接口的编程在修改时不需要修改代码,而是扩展的开放-封闭原则提供了实现的原理保障。\r\n 里氏代换原则:子类继承父类,则子类可以完全代替父类的所有功能,而不会被使用者察觉。\r\n 对于外部只能调用父类的所有public方法,子类则都可以继承过来。\r\n 里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:\r\n 2、子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。\r\n 3、子类中可以增加自己特有的方法。\r\n 4、当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。\r\n 5、当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。\r\n\r\n 反面案例:收音机!芯片,喇叭什么的一大堆焊接在一起,超高的耦合度!', 1397782506, 17, 10, 0),
(20, 8, '李轩Lane', '面向对象的洗礼:设计模式(六)之装饰模式', '装饰模式,动态的给一个对象添加一些额外的职责,就增加的功能来说,装饰模式比生成子类更为灵活。设计模式之装饰模式。每个装饰对象的实现和如何使用这个对象分离了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链中。', '装饰模式_设计模式_设计模式之装饰模式', '装饰模式,动态的给一个对象添加一些额外的职责,就增加的功能来说,装饰模式比生成子类更为灵活。设计模式之装饰模式。', '装饰模式,设计模式,设计模式之装饰模式', 'PHP|设计模式|装饰模式', 630, '装饰模式,动态的给一个对象添加一些额外的职责,就增加的功能来说,装饰模式比生成子类更为灵活。设计模式之装饰模式。每个装饰对象的实现和如何使用这个对象分离了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链中。\r\n[code]\r\n<?php\r\nabstract class Component{\r\n public function operation(){\r\n\r\n }\r\n}\r\nclass ConcreteComponent extends Component{\r\n public function operation(){\r\n echo ''具体对象操作'';\r\n }\r\n}\r\n\r\nabstract class Decorator extends Component{\r\n protected $component;\r\n public function setComponet($objComponent){\r\n $this->component = $objComponent;\r\n }\r\n public function sonOperation(){\r\n if(empty($this->component)){\r\n $this->operation();\r\n }\r\n }\r\n}\r\nclass ConcreteDecoratorA extends Decorator{\r\n private $addState;\r\n public function concteteOperation(){\r\n $this->operation();\r\n $this->addState = ''New State'';\r\n echo ''具体装饰对象A的操作'';\r\n }\r\n}\r\nclass ConcreteDecoratorB extends Decorator{\r\n public function concreteOperation(){\r\n $this->operation();\r\n $this->addBehavior();\r\n echo ''具体操作对象B的操作'';\r\n }\r\n public function addBehavior(){\r\n\r\n }\r\n}\r\n$cObj = new ConcreteComponent();\r\n$d1Obj = new ConcreteDecoratorA();\r\n$d2Obj = new ConcreteDecoratorB();\r\n//装饰的方法是,先实例化对象c,用d1包装c,用d2包装d1,最终通过d2来执行operation\r\n$d1Obj->setComponet($cObj);\r\n$d2Obj->setComponet($d1Obj);\r\n$d2Obj->sonOperation();\r\n?>\r\n[/code]\r\n\r\n 装饰模式是为已有的功能动态的添加更多功能的一种方式。当系统需要新功能的时候,是向旧类中添加新代码。这些新代码通常装饰了原有的类的核心职责或主要行为。新加入的代码仅仅是在满足一定特定条件下才会被需要。而装饰模式提供了一个解决方案。把每个要装饰的功能放在单独类中。需要执行特殊行为时,客户端代码可以有选择的有顺序的去使用装饰功能包装对象。\r\n 装饰模式就是把类中的装饰功能删掉,简化原类,把核心职责和装饰区分开。\r\n 场景:数据加密和数据过滤是我们在写入数据库前要做的工作,那么先加密再过滤和先过滤再加密,结果肯定是不一样的。所以,保证加密和过滤这2个类彼此独立,如果使用,在客户端进行不同的组合。', 1397892233, 10, 9, 0),
(21, 8, '李轩Lane', '面向对象的洗礼:设计模式(七)之代理模式', '代理模式,是为其他对象提供一种代理以控制对这个对象的访问,代理模式是设计模式的一种。应用较为广泛,是一个对象需要访问另一个对象,出于某种原因或目的,在两个对象之间添加了一个中间对象。A对象访问B对象的方法,B对象的该方法实际是调用的C对象的方法,间接的完成了A对象对C对象的访问。这种模式叫做代理模式。', '代理模式_设计模式_设计模式之代理模式', '代理模式,是为其他对象提供一种代理以控制对这个对象的访问,代理模式是设计模式的一种。是很常见的一种代码书写方法。', '代理模式,设计模式,设计模式之代理模式', 'PHP|设计模式|代理模式', 466, '代理模式,是为其他对象提供一种代理以控制对这个对象的访问,代理模式是设计模式的一种。应用较为广泛,是一个对象需要访问另一个对象,出于某种原因或目的,在两个对象之间添加了一个中间对象。A对象访问B对象的方法,B对象的该方法实际是调用的C对象的方法,间接的完成了A对象对C对象的访问。这种模式叫做代理模式。\r\n 以PHP为代码环境,实现设计模式中的代理模式。\r\n[code]\r\n<?php\r\nabstract class Subject(){\r\nabstract class Subject(){\r\n public function actionA();\r\n public function actionB(){;\r\n public function actionC();\r\n}\r\nclass Substance implements Subject(){\r\n public function actionA(){\r\n echo ''方法A的实现''; \r\n }\r\n public function actionB(){\r\n echo ''方法B的实现'';\r\n }\r\n public function actionC(){\r\n echo ''方法C的实现'';\r\n } \r\n}\r\nclass Proxy implements Subject(){\r\n $protected $obj;\r\n public function __construct(){\r\n $obj = new Substance();\r\n }\r\n public function actionA(){\r\n $this->obj->actionA;\r\n }\r\n public function actionB(){\r\n $this->obj->actionB;\r\n }\r\n public function actionC(){\r\n $this->obj->actionC;\r\n } \r\n}\r\n//客户端/接口\r\n$obj = new Proxy();\r\n$obj->actionA();\r\n$obj->actionB();\r\n$obj->actionC(); \r\n}\r\n?>\r\n[/code]\r\n 代理模式的使用场景:(整理自大话设计模式一书)\r\n 第一、远程代理,为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。\r\n 第二、虚拟代理,是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的对象。\r\n 第三、安全代理,用来控制真实对象的访问权限。\r\n 第四、智能指引,是指当调用真实对象时,代理处理另外一些事情。(我的理解是,比如底层有一个封装好的Mysql类,在上层应用层读取数据库时,先经过一个代理类,可以检查数据完整性,参数合法性,计数器等等,然后由代理类调用真实的Mysql类)', 1397995422, 12, 10, 0),
(22, 8, '李轩Lane', '面向对象的洗礼:设计模式(八)之工厂方法', '工厂方法来源于简单工厂模式,是简单工厂模式的一个衍生品。核心的工厂类不再进行类的实例化,核心工厂类不再负责产品的创建,核心的工厂类只负责子类的接口,使核心工厂类抽象化,成为一个抽象工厂。', '工厂方法_设计模式_设计模式之工厂方法', '工厂方法来源于简单工厂模式,是简单工厂模式的一个衍生品。核心的工厂类不再进行类的实例化,核心工厂类不再负责产品的创建,核心的工厂类只负责子类的接口,使核心工厂类抽象化,成为一个抽象工厂。', '工厂方法,设计模式,设计模式之工厂方法', 'PHP|设计模式|工厂方法', 707, '工厂方法:定义一个工厂接口,用来创建产品对象,将实际创建工作推迟到子类当中。\r\n 工厂方法来源于简单工厂模式,是简单工厂模式的一个衍生品。核心的工厂类不再进行类的实例化,核心工厂类不再负责产品的创建,核心的工厂类只负责子类的接口,使核心工厂类抽象化,成为一个抽象工厂。\r\n 工厂方法的优点:在简单工厂模式的基础上再次对核心工厂类进行抽象,在需要添加新的产品时,更好的依赖于开放-封闭原则,不需要修改具体的工厂角色即可扩展。\r\n 场景:简单计算器。以PHP为代码实现环境。\r\n简单工厂模式:\r\n[code]\r\n<?php\r\nclass Calculator{\r\n public $numberA;\r\n public $numberB;\r\n public $result;\r\n public function returnResult(){\r\n\r\n }\r\n}\r\nclass Add extends Calculator{\r\n public function __construct($a, $b){\r\n $this->numberA = $a;\r\n $this->numberB = $b;\r\n }\r\n public function returnResult(){\r\n $this->result = $this->numberA + $this->numberB;\r\n return $this->returnResult();\r\n }\r\n}\r\nclass Sub extends Calculator{\r\n public function __construct($a, $b){\r\n $this->numberA = $a;\r\n $this->numberB = $b;\r\n }\r\n public function returnResult(){\r\n $this->result = $this->numberA - $this->numberB;\r\n return $this->returnResult();\r\n }\r\n}\r\nclass Factory{\r\n public function calculatorFactory($operator, $numberA, $numberB){\r\n $obj = '''';\r\n switch($operator){\r\n case ''+'':\r\n $obj = new Add($numberA, $numberB);\r\n break;\r\n case ''-'':\r\n $obj = new Sub($numberA, $numberB);\r\n break;\r\n }\r\n $result = $obj->returnResult();\r\n return $result;\r\n }\r\n}\r\n//客户端/接口\r\n$operation = isset($_GET[''operation'']) ? $_GET[''operation''] : ''+'';\r\n$numberA = $_GET[''numberA''];\r\n$numberB = $_GET[''numberB''];\r\n$obj = new Factory();\r\necho $obj->calculatorFactory($operation, $numberA, $numberB);\r\n?>\r\n[/code]\r\n工厂方法:\r\n[code]\r\n<?php\r\nclass Calculator{\r\n public $numberA;\r\n public $numberB;\r\n public $result;\r\n public function returnResult(){}\r\n}\r\nclass Add extends Calculator{\r\n public function __construct($a, $b){\r\n $this->numberA = $a;\r\n $this->numberB = $b;\r\n }\r\n public function returnResult(){\r\n $this->result = $this->numberA + $this->numberB;\r\n return $this->returnResult();\r\n }\r\n}\r\nclass Sub extends Calculator{\r\n public function __construct($a, $b){\r\n $this->numberA = $a;\r\n $this->numberB = $b;\r\n }\r\n public function returnResult(){\r\n $this->result = $this->numberA - $this->numberB;\r\n return $this->returnResult();\r\n }\r\n}\r\nclass Factory extends Calculator{\r\n public static function create($class, $numberA, $numberb){\r\n return new $class($numberA, $numberb);\r\n }\r\n}\r\n//客户端/接口\r\n$operator = isset($_GET[''operation'']) ? $_GET[''operation''] : ''+'';\r\n$numberA = $_GET[''numberA''];\r\n$numberB = $_GET[''numberB''];\r\nswitch($operator){\r\n case ''+'':\r\n $class = ''objAdd'';\r\n break;\r\n case ''-'':\r\n $class = ''objSub'';\r\n break;\r\n}\r\n$obj = Factory::create($class, $numberA, $numberb);\r\necho $obj->returnResult();\r\n?>\r\n[/code]\r\n\r\n简单工厂模式VS工厂方法模式:\r\n简单工厂的选择在工厂类,', 1398039722, 10, 11, 0),
(23, 8, '李轩Lane', '面向对象的洗礼:设计模式(九)之原型模式', '原型模式提取重复功能,避免了程序员喜欢复制粘贴的坏习惯。设计模式中的原型模式就是,用原型实例指定创建对象的重力,通过拷贝这些原型来创建新的对象从一个对象再创建另外一个可定制的对象,而且不需要知道创建的任何细节。', '原型模式_设计模式_设计模式之原型模式', '原型模式提取重复功能,避免了程序员喜欢复制粘贴的坏习惯。设计模式中的原型模式就是,用原型实例指定创建对象的重力,通过拷贝这些原型来创建新的对象。', '原型模式,设计模式,设计模式之原型模式', 'PHP|设计模式|原型模式', 476, '原型模式提取重复功能,避免了程序员喜欢复制粘贴的坏习惯。设计模式中的原型模式就是,用原型实例指定创建对象的重力,通过拷贝这些原型来创建新的对象从一个对象再创建另外一个可定制的对象,而且不需要知道创建的任何细节。\r\n 浅复制 VS 深复制:\r\n 浅复制是对数字,字符串等类型进行传值复制,而对对象来讲是引用复制,即只是对内存地址进行赋值而不是新建一个对象的变量。在浅复制中,对一个对象的属性改变,另一个对象的该属性也会被改变,类比于C语言的指针,PHP在调用方法时&$var的传递。\r\n 以PHP为代码环境。\r\n[code]\r\n<?php\r\n//家庭类\r\nclass Home{\r\n public $money;\r\n public function __construct($money){\r\n $this->money = $money;\r\n }\r\n}\r\n//家庭成员类\r\nclass member{\r\n public $id;\r\n public $name;\r\n public $obj;\r\n public function __construct($id, $name, Home $obj){\r\n $this->setId($id);\r\n $this->setName($name);\r\n $this->obj = $obj;\r\n }\r\n public function setId($id){\r\n $this->id = $id;\r\n }\r\n public function setName($name){\r\n $this->name = $name;\r\n }\r\n public function display(){\r\n echo ''ID为'' . $this->id . '',名称为'' . $this->name . '',资产为'' . $this->obj->money . ''<br>'';\r\n }\r\n public function __clone(){\r\n //深度复制(克隆),因为克隆只能克隆数字,字符串等,对对象变量是引用传值。 \r\n $this->obj = clone $this->obj;\r\n }\r\n}\r\n//客户端/接口\r\n$obj1 = new member(1, ''小轩'', new Home(''10000''));\r\n$obj1->display();\r\n$obj2 = clone $obj1;\r\n$obj2->setId(2);\r\n$obj2->setName(''小玮'');\r\n$obj2->obj->money = 2000;\r\n$obj2->display();\r\n//根据这句输出可以看到,对象1和对象2值是不一样的,删掉上面的注释部分再看,又是一样的了,这就是深复制和浅复制。\r\n$obj1->display();\r\n?>\r\n[/code]', 1398088115, 10, 8, 0),
(24, 8, '李轩Lane', '面向对象的洗礼:设计模式(十)之模板方法模式', '模板方法模式,是最为常见,也是使用最为广泛的一种设计模式,很多程序猿都不知道,自己随便写的代码,也是一种设计模式。如果只能学习一种设计模式的话,那么就应该学习模板模式。顾名思义,模板模式,就是有一个固定的,现成的模板,往里面套东西呗。比如PPT,WORD,EXCEL等,Microsoft为我们提供了大量的模板。可以直接套用,也可以略做修改。总之,比我们自己全新做要省很多事儿。', '模板方法模式_设计模式_设计模式之模板方法模式', '模板方法模式,是最为常见,也是使用最为广泛的一种设计模式,很多程序猿都不知道,自己随便写的代码,也是一种设计模式。如果只能学习一种设计模式的话,那么就应该学习模板模式。', '模板方法模式,设计模式,设计模式之模板方法模式', 'PHP|设计模式|模板方法模式', 536, '模板方法模式,是最为常见,也是使用最为广泛的一种设计模式,很多程序猿都不知道,自己随便写的代码,也是一种设计模式。如果只能学习一种设计模式的话,那么就应该学习模板模式。\r\n 模板模式:在一个方法里定义算法的骨架,将一些步骤延迟到其子类。顾名思义,模板模式,就是有一个固定的,现成的模板,往里面套东西呗。比如PPT,WORD,EXCEL等,Microsoft为我们提供了大量的模板。可以直接套用,也可以略做修改。总之,比我们自己全新做要省很多事儿。\r\n 抽出多个类的共同特性,成为一个父类,父类根据需求封装好一个算法骨架,然后子类调用父类即可。\r\n 以PHP为代码环境,\r\n[code]\r\n<?php\r\nclass TestPaper{\r\n public $name;\r\n public $classes;\r\n public function __construct($name, $classes){\r\n $this->name = $name;\r\n $this->classes = $classes;\r\n }\r\n public function display(){\r\n echo ''姓名:'' . $this->name . '', 班级:'' . $this->classes;\r\n $this->separate();\r\n }\r\n public function title1($answer){\r\n echo ''题目一:******'';\r\n echo ''答案:'' . $this->answer($answer);\r\n $this->separate();\r\n }\r\n public function title2($answer){\r\n echo ''题目二:******'';\r\n echo ''答案:'' . $this->answer($answer);\r\n $this->separate();\r\n }\r\n public function answer($answer){\r\n return $answer;\r\n }\r\n public function separate(){\r\n echo ''<br>'';\r\n }\r\n}\r\nclass studentA extends TestPaper{\r\n public function __construct($name, $classes){\r\n parent::__construct($name, $classes);\r\n }\r\n public function answerTestPaper(){\r\n $this->display();\r\n $this->title1(''C'');\r\n $this->title1(''B'');\r\n }\r\n}\r\nclass studentB extends TestPaper{\r\n public function __construct($name, $classes){\r\n parent::__construct($name, $classes);\r\n }\r\n public function answerTestPaper(){\r\n $this->display();\r\n $this->title1(''A'');\r\n $this->title1(''D'');\r\n }\r\n}\r\n$studentA = new studentA(''小明'', ''一'');\r\n$studentA->answerTestPaper();\r\n$studentB = new studentB(''小红'', ''二'');\r\n$studentB->answerTestPaper();\r\n?>\r\n[/code]', 1398126858, 10, 11, 0),
(25, 8, '李轩Lane', '面向对象的洗礼:设计模式(十一)之迪米特法则', '迪米特法则,再次强调了面向对象的特性之一:封装。不需要知道具体如何实现的细节,只需要调用某个类的方法,得到预期的结果。尽可能少的使用public,降低成员的访问权限。可以更好降低类与类之间的耦合度。程序设计时,修改一个越弱耦合的类,对系统造成的影响就会越小,耦合度越低,越利于复用。这就是迪米特法则的根本思想。', '迪米特法则_设计模式_设计模式之迪米特法则', '迪米特法则,再次强调了面向对象的特性之一:封装。设计模式的原则之一的迪米特法则,它的根本思想是程序设计时,修改一个越弱耦合的类,对系统造成的影响就会越小,耦合度越低,越利于复用。', '迪米特法则,设计模式,设计模式之迪米特法则', 'PHP|设计模式|迪米特法则', 486, '面向对象的特性之一:封装。不需要知道具体如何实现的细节,只需要调用某个类的方法,得到预期的结果。尽可能少的使用public,降低成员的访问权限。可以更好降低类与类之间的耦合度。程序设计时,修改一个越弱耦合的类,对系统造成的影响就会越小,耦合度越低,越利于复用。这就是迪米特法则的根本思想。\r\n 依赖接口而不是依赖实现,在弱耦合、低权限的基础上,完全不需要关心接口的实现细节,这也就是依赖倒转原则。面向对象的原则和面向对象的特性是不对立的。\r\n 迪米特法则:如果两个类,不需要直接进行两个类之间的通信,那么,这两个类就不应该直接发生作用和求情,如果一个类在特定条件下需要调用另一个类,那么,可以通过第三个类来实现,转发这个调用。\r\n 是不是又用点像代理模式?代理模式是针对对象的,代理类实例化真实类,调用真实类的方法。而迪米特法则是一个类调用另一个类,然后这个另一个类再调用另另一个类。', 1398174088, 9, 9, 0),
(26, 8, '李轩Lane', '面向对象的洗礼:设计模式(十二)之外观模式', '外观模式其实非常容易用到,是对迪米特法则的一种应用:降低类的耦合度,添加中间件。也是对依赖倒转原则的完美体现:针对接口的编程。作为一个中间件,降低底层接口和使用者(客户端的)耦合度。', '外观模式_设计模式_设计模式之外观模式', '外观模式其实非常容易用到,是对迪米特法则的一种应用:降低类的耦合度,添加中间件。也是对依赖倒转原则的完美体现:针对接口的编程。', '外观模式,设计模式,设计模式之外观模式', 'PHP|设计模式|外观模式', 532, '外观模式其实非常容易用到,是对迪米特法则的一种应用:降低类的耦合度,添加中间件。也是对依赖倒转原则的完美体现:针对接口的编程。\r\n 外观模式:再次针对某个接口封装一个高层类,实现一个高层接口,按某种算法或使用方式整合底层接口类,使得底层的接口更加容易使用,也降低了底层接口和客户端的耦合度。\r\n 场景:调用数据库。以PHP为代码环境,以Mysql为数据库环境。\r\n[code]\r\n<?php\r\nclass MysqlDB{\r\n private $conn;\r\n public function __construct($host, $username, $password, $dbName){\r\n $this->conn($host, $username, $password);\r\n $this->selectDb($dbName);\r\n }\r\n private function conn($host, $username, $password){\r\n $this->conn = mysql_connect($host, $username, $password);\r\n }\r\n private function selectDb($dbName){\r\n mysql_select_db($dbName, $this->conn);\r\n }\r\n public function query($sql){\r\n return mysql_query($sql);\r\n }\r\n public function fetchArray($queryResult){\r\n return mysql_fetch_array($queryResult);\r\n }\r\n public function fetchAssoc($queryResult){\r\n return mysql_fetch_assoc($queryResutl);\r\n }\r\n}\r\nclass Facade{\r\n private $mysqlObj;\r\n public function __construct($host, $username, $password, $dbName){\r\n $this->mysqlObj = new MysqlDB($host, $username, $password, $dbName);\r\n }\r\n public function get($tableName){\r\n $sql = ''SELECT * FROM '' . $tableName;\r\n $queryResult = $this->mysqlObj->query($sql);\r\n $fetchArr = $this->myqlObj->fetchAssoc($queryResult);\r\n return $fetchArr\r\n }\r\n}\r\n//客户端/接口\r\n$obj = new Facade(''localhost'', ''root'', ''root'', ''db_name'');\r\n$list = $obj->get(''user_info'');\r\n?>\r\n[/code]\r\n\r\n 常见的使用场景:\r\n 1、开发的初期阶段,有意识的建立中间件,将不同的两层分离,在层与层之间建立外观。\r\n 2、在开发阶段,某个类会根据需求的不断变更等原因使类变得更加复杂而庞大,增加一个外观类,使的使用者和这个庞大负责的类耦合降低。\r\n 3、历史遗留问题。需要用到遗留的复杂逻辑的类,直接调用是不好的,所以需要一个中间件(外观模式的外观类)来调用这个复杂类,而使用者调用外观类即可。\r\n 可以理解为,外观模式的外观类,是一个入口,使用者调用外观类,外观类调用底层的类。', 1398174892, 13, 17, 0),
(27, 8, '李轩Lane', '面向对象的洗礼:设计模式(十三)之建造者模式', '建造者模式,也叫生成器模式。是设计模式的一种。某个复杂算法类,在方法调用上是顺序稳定的,但是具体属性不同,此时可以使用建造者模式。在建造者模式这一的设计模式种,第一个类builder是各种创建方法的抽象接口。ConcreteBuilder调用Builder的接口来装配。提供对外的接口。ProductA是A产品类,调用ConcreteBuilder实现了具体的产品A的实现方法,也就是需要被构造的那个复杂的对象。Director就是我们的向导类,根据客户的需求生成产品A、产品B、产品C。', '建造者模式_设计模式_设计模式之建造者', '建造者模式,也叫生成器模式。是设计模式的一种。某个复杂算法类,在方法调用上是顺序稳定的,但是具体属性不同,此时可以使用建造者模式。', '建造者模式,设计模式,设计模式之建造者', 'PHP|设计模式|建造者模式', 414, '建造者模式,也叫生成器模式。是设计模式的一种。某个复杂算法类,在方法调用上是顺序稳定的,但是具体属性不同,此时可以使用建造者模式。\r\n 建造者模式:一个复杂的对象,我们把它的构造和它的表示分离,可以实现同样的构造,而产生多种不同的表示,这种设计模式我们把它叫做建造者模式,也被成为生成器模式。顾名思义,在一个厂房中批量生成。\r\n 在定义和开发时,必须要满足:1、我们开发的类,允许被它的对象有多种不同的表示。2、当创建复杂对象的算法,应该独立于该对象的组成部分和该对象的装配方式。\r\n 在建造者模式这一的设计模式种,第一个类builder是各种创建方法的抽象接口。ConcreteBuilder调用Builder的接口来装配。提供对外的接口。ProductA是A产品类,调用ConcreteBuilder实现了具体的产品A的实现方法,也就是需要被构造的那个复杂的对象。Director就是我们的向导类,根据客户的需求生成产品A、产品B、产品C。\r\n 场景:麦当劳,汉堡和批萨,收银员就是向导类。以PHP为代码环境。\r\n[code]\r\n<?php\r\n//麦当劳,抽象接口类\r\ninterface McDonald{\r\n public function yuanLiao();\r\n public function nieXingZhuang();\r\n public function jiaRe();\r\n}\r\n//汉堡,就是产品A类\r\nclass Hamburger implements McDonald{\r\n public function yuanLiao(){\r\n echo ''采购原料:面+肉+生菜+酱'';\r\n $this->separate();\r\n }\r\n public function nieXingZhuang(){\r\n echo ''捏成蓬松的圆球形状'';\r\n $this->separate();\r\n }\r\n public function jiaRe(){\r\n echo ''加热10分种'';\r\n $this->separate();\r\n }\r\n private function separate(){\r\n echo ''<br>'';\r\n }\r\n}\r\n//薯条,就是产品B类\r\nclass FrenchFries implements McDonald{\r\n public function yuanLiao(){\r\n echo ''采购原料:土豆'';\r\n $this->separate();\r\n }\r\n public function nieXingZhuang(){\r\n echo ''切成细长条'';\r\n $this->separate();\r\n }\r\n public function jiaRe(){\r\n echo ''加热15分种'';\r\n $this->separate();\r\n }\r\n private function separate(){\r\n echo ''<br>'';\r\n }\r\n}\r\n//收银员,就是向导类\r\nclass Cashier{\r\n public function createProduct($productObj){\r\n $productObj->yuanLiao();\r\n $productObj->nieXingZhuang();\r\n $productObj->jiaRe();\r\n return ''制作完成,可以上桌了'';\r\n }\r\n}\r\n//客户端/接口\r\n$cashier = new Cashier();\r\n$cashier->createProduct(new Hamburger());\r\n$cashier->createProduct(new FrenchFries());\r\n?>\r\n[/code]', 1398214218, 10, 10, 0),
(28, 8, '李轩Lane', '面向对象的洗礼:设计模式(十四)之观察者模式', '观察者模式,又叫做订阅-发布模式。当一个对象的改变需要同时改变多个对象的时候,可以使用法不这模式。设计模式中的观察者模式,就是为了解除类之间的耦合,使双方都依赖于抽象而不是依赖于具体。在实际生活中,比如我们更换了手机号,需要通知大家的时候,我们就是主题,或者通知者,而需要通知的人就是观察者列表,一条短信的群发告诉大家,就是观察者模式的应用。', '观察者模式_设计模式_设计模式之观察者模式', '观察者模式,又叫做订阅-发布模式。当一个对象的改变需要同时改变多个对象的时候,可以使用法不这模式。设计模式中的观察者模式,就是为了解除类之间的耦合,使双方都依赖于抽象而不是依赖于具体。', '观察者模式,设计模式,设计模式之观察者模式', 'PHP|设计模式|观察者模式', 485, '观察者模式,又叫做订阅-发布模式。当一个对象的改变需要同时改变多个对象的时候,可以使用法不这模式。设计模式中的观察者模式,就是为了解除类之间的耦合,使双方都依赖于抽象而不是依赖于具体。在实际生活中,比如我们更换了手机号,需要通知大家的时候,我们就是主题,或者通知者,而需要通知的人就是观察者列表,一条短信的群发告诉大家,就是观察者模式的应用。\r\n[code]\r\n<?php\r\n//主题者、通知者抽象类。\r\nabstract class Subject{\r\n private $observerList = array();\r\n public function add(&$obj, $action){\r\n $this->observerList[] = array(''obj''=>$obj, ''action''=>$action);\r\n }\r\n public function notice(){\r\n foreach($this->observerList as $observer){\r\n $obj = $observer[''obj''];\r\n $action = $observer[''action''];\r\n $obj->$action;\r\n }\r\n }\r\n}\r\n//具体的通知者\r\nclass I extends Subject{\r\n private $status;\r\n public function getStatus(){\r\n return $this->status;\r\n }\r\n public function setStatus($status){\r\n $this->status = $status;\r\n }\r\n}\r\n//观者者抽象类\r\nabstract class Observer{\r\n public function sendSms(){\r\n \r\n }\r\n}\r\n//具体的观察者\r\nclass ConcreteObserver extends Observer{\r\n private $name;\r\n private $status;\r\n private $objSubject;\r\n public function __construct($name, $objSubject){\r\n $this->name = $name;\r\n $this->objSubject = $objSubject;\r\n }\r\n public function sendSms(){\r\n $status = $this->objSubject->getStatus();\r\n echo ''观察者''.$this->name.''收到的状态是''.$status.''<br>'';\r\n }\r\n}\r\n//客户端/接口\r\n$i = new I();\r\n$i->setStatus(''更换手机号码了。'');\r\n$friend1 = new ConcreteObserver(''小明'', $i);\r\n$friend2 = new ConcreteObserver(''小红'', $i);\r\n$friend3 = new ConcreteObserver(''小黄'', $i);\r\n$i->add($friend1, ''sendSms'');\r\n$i->add($friend2, ''sendSms'');\r\n$i->add($friend3, ''sendSms'');\r\n$i->notice();\r\n?>\r\n[/code]', 1398261969, 10, 14, 0),
(29, 8, '李轩Lane', '面向对象的洗礼:设计模式(十五)之抽象工厂模式', '抽象工厂模式,是工厂方法模式的演变,而工厂方法模式,是简单工厂模式的进化。抛弃了应用的条件控制语句,无论是switch还是if-ifelse。是设计模式的一种。抽线工厂模式来自于方法模式和简单工厂模式的进化与整合,其实,我已经要疯了,23种设计模式,现在已经出现了三种工厂模式。', '抽象工厂模式_设计模式_设计模式之抽象工厂模式', '抽象工厂模式,是工厂方法模式的演变,而工厂方法模式,是简单工厂模式的进化。抛弃了应用的条件控制语句,无论是switch还是if-ifelse。是设计模式的一种。', '抽象工厂模式,设计模式,设计模式之抽象工厂模式', 'PHP|设计模式|抽象工厂模式', 406, '抽象工厂模式,是工厂方法模式的演变,而工厂方法模式,是简单工厂模式的进化。抛弃了应用的条件控制语句,无论是switch还是if-ifelse。是设计模式的一种。\r\n 抽线工厂模式来自于方法模式和简单工厂模式的进化与整合,其实,我已经要疯了,23种设计模式,现在已经出现了三种工厂模式。\r\n 抽象工厂模式:提供一个创建一系列相关的、相互依赖的对象接口,而无需指定他们的具体类。\r\n 对于面向过程的编程,以及套在class里的面向对象的编程,修改起来是大批量的,是非常丑陋的。我常常告诫自己,编程是一门艺术,每个程序员都是艺术家,写出优美的,有艺术感的代码,并且是高效的,低成本的,这就是编程之美!\r\n 场景:原本是mysql,现在要换成oracle。以PHP为代码环境。现在有两张表,一个是用户表user,一个是公司表company。\r\n[code]\r\n<?php\r\nclass Db{\r\n private static $dbName = ''mysql'';\r\n public static function createUserDbObj(){\r\n $className = self::$dbName . ''UserDbModel'';\r\n return new $className();\r\n }\r\n public static function createCompanyDbObj(){\r\n $className = self::$dbName . ''CompanyDbModel'';\r\n return new $className();\r\n }\r\n}\r\ninterface User{\r\n public function get();\r\n public function set();\r\n}\r\nclass mysqlUserDbModel implements User{\r\n public function get(){\r\n echo ''从Mysql中查找用户记录<br>'';\r\n }\r\n public function set(){\r\n echo ''从Mysql中添加用户记录<br>'';\r\n }\r\n}\r\nclass oracleUserDbModel implements User{\r\n public function get(){\r\n echo ''从Oracle中查找用户记录<br>'';\r\n }\r\n public function set(){\r\n echo ''从Oracle中添加用户记录<br>'';\r\n }\r\n}\r\ninterface Company{\r\n public function get();\r\n public function set();\r\n}\r\nclass mysqlCompanyDbModel implements User{\r\n public function get(){\r\n echo ''从Mysql中查找公司记录<br>'';\r\n }\r\n public function set(){\r\n echo ''从Mysql中添加公司记录<br>'';\r\n }\r\n}\r\nclass oracleCompanyDbModel implements User{\r\n public function get(){\r\n echo ''从Oracle中查找公司记录<br>'';\r\n }\r\n public function set(){\r\n echo ''从Oracle中添加公司记录<br>'';\r\n }\r\n}\r\n//客户端/接口\r\n$userDbObj = Db::createUserDbObj();\r\n$companyDbObj = Db::createCompanyDbObj();\r\n$userDbObj->get();\r\n$userDbObj->set();\r\n$companyDbObj->get();\r\n$companyDbObj->set();\r\n[/code]\r\n 现在代码的方式,是把选择数据库给写死到程序中了(Db类), 我们可以以更加灵活的方式,比如:\r\n 1、配置\r\n[code]\r\nconfig.php\r\n<?php\r\ndefine(''DB_NAME'', ''mysql'');\r\n[/code]\r\n 2、文件\r\n[code]\r\n$f = fopen(''config'', ''r'');\r\n$config = '''';\r\nwhile(!feof($f)){\r\n $config .= fgets($f);\r\n $config .= '' '';\r\n}\r\n//----------------我是分割线-----------\r\n$config = file_get_contents(''config'');\r\n//-------------------------------------------\r\n//$config = ''dbtype:mysql|username:root|password:root'';\r\n$config = explode(''|'', $config);\r\nforeach($config as $k=>$c){\r\n $data = explode('':'', $c);\r\n unset($config[$k]);\r\n $config[$data[0]] = $data[1];\r\n}\r\nprint_r($config);\r\n[/code]\r\n 个人认为:这种方式的数据库应用代码设计,仍旧是很繁琐的,每增加一张表,需要增加各个类型的数据库类各一个。所以,本例仅仅是为了演示说明抽象工厂模式。', 1398345637, 8, 7, 0),
(30, 8, '李轩Lane', '面向对象的洗礼:设计模式(十六)之状态模式', '状态模式是根据状态来执行不同的功能,通常以switch和if-ifelse来逻辑判断。面向对象设计,它的目的就是希望代码能够根据责任、功能来进行分解,不再是一大长串。状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的时候,把状态的判断转移到表示不同状态的一系列类当中,把复杂的判断逻辑简化。', '状态模式_设计模式_设计模式之状态模式', '态模式是根据状态来执行不同的功能,通常以switch和if-ifelse来逻辑判断。状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的时候,把状态的判断转移到表示不同状态的一系列类当中,把复杂的判断逻辑简化。', '状态模式,设计模式,设计模式之状态模式', 'PHP|设计模式|状态模式', 477, '状态模式是根据状态来执行不同的功能,通常以switch和if-ifelse来逻辑判断。面向对象设计,它的目的就是希望代码能够根据责任、功能来进行分解,不再是一大长串。状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的时候,把状态的判断转移到表示不同状态的一系列类当中,把复杂的判断逻辑简化。\r\n 状态模式:当一个对象内在的状态改变时允许改变他的行为,这个对象看起来像是改变了其类。\r\n 当一个对象运行时,该执行什么方法,是取决于它的状态的时候,我们不用臃肿的条件判断语句,而是使用状态模式。\r\n 场景:人的行为,早上吃早饭,然后走路,上班,吃午饭,上班,走路,回家吃晚饭,睡觉。用条件控制来实现,if(time()==8点){ 吃早饭 }else if()....看看GoF的状态模式如何实现人的行为。以PHP为代码环境。\r\n[code]\r\n<?php\r\nclass Person{\r\n private $state;\r\n private $time;\r\n public function __construct(){\r\n $this->state = new Breakfast();\r\n }\r\n public function getTime(){\r\n return $this->time;\r\n }\r\n public function setTime($time){\r\n $this->time = $time;\r\n }\r\n public function getState(){\r\n return $this->state;\r\n }\r\n public function setState($state){\r\n $this->state = $state;\r\n }\r\n public function behavior(){\r\n $this->state->behavior($this);\r\n }\r\n}\r\nclass Breakfast{\r\n public function behavior($personObj){\r\n if($personObj->getTime() < 8){\r\n echo ''吃早餐<br>'';\r\n }else{\r\n $personObj->setState(new Walk());\r\n $personObj->behavior();\r\n }\r\n }\r\n}\r\nclass Walk{\r\n public function behavior($personObj){\r\n if($personObj->getTime() < 9 || ($personObj->getTime() > 18 && $personObj->getTime() < 19)){\r\n echo ''走路<br>'';\r\n }else{\r\n if($personObj->getTime() > 9 && $personObj->getTime() < 18){\r\n $personObj->setState(new Work());\r\n $personObj->behavior();\r\n }else{\r\n $personObj->setState(new Dinner());\r\n $personObj->behavior();\r\n }\r\n }\r\n }\r\n}\r\nclass Work{\r\n public function behavior($personObj){\r\n if($personObj->getTime() < 12 || ($personObj->getTime() > 13 && $personObj->getTime() < 18)){\r\n echo ''工作<br>'';\r\n }else{\r\n if($personObj->getTime() < 13){\r\n $personObj->setState(new Lunch());\r\n $personObj->behavior();\r\n }else{\r\n $personObj->setState(new Walk());\r\n $personObj->behavior();\r\n }\r\n }\r\n }\r\n}\r\nclass Lunch{\r\n public function behavior($personObj){\r\n if($personObj->getTime() < 13){\r\n echo ''吃午餐<br>'';\r\n }else{\r\n $personObj->setState(new work());\r\n $personObj->behavior();\r\n }\r\n }\r\n}\r\nclass Dinner{\r\n public function behavior($personObj){\r\n if($personObj->getTime() < 20){\r\n echo ''吃晚餐<br>'';\r\n }else{\r\n $personObj->setState(new Sleep());\r\n $personObj->behavior();\r\n }\r\n }\r\n}\r\nclass Sleep{\r\n public function behavior($personObj){\r\n echo ''睡觉<br>'';\r\n exit;\r\n }\r\n}\r\n//客户端/接口\r\n$personObj = new Person();\r\n//时间表\r\n$timeList = array(7, 8.5, 10, 12.5, 15, 18.5, 19.5);\r\nforeach($timeList as $time){\r\n $personObj->setTime($time);\r\n $personObj->behavior();\r\n}\r\n[/code]', 1398348266, 11, 10, 0),
(31, 8, '李轩Lane', '面向对象的洗礼:设计模式(十七)之适配器模式', '适配器模式,尽管是一种常见的设计模式,但是有点亡羊补牢的感觉。不是首选的设计模式。适配器模式是连接两个类的中间件,当一个类想要调用某一个类的接口时,发现尽管这个类的接口可以实现想要的功能,但是却不能用。比如因为格式的问题等等,这时候需要一个中间件来充当转换器,这就是适配器模式。', '适配器模式_设计模式_设计模式之适配器模式', '适配器模式,尽管是一种常见的设计模式,但是有点亡羊补牢的感觉。不是首选的设计模式。适配器模式是连接两个类的中间件,当一个类想要调用某一个类的接口时,发现尽管这个类的接口可以实现想要的功能,但是却不能用。比如因为格式的问题等等,这时候需要一个中间件来充当转换器,这就是适配器模式。', '适配器模式,设计模式,设计模式之适配器模式', 'PHP|设计模式|适配器模式', 478, '适配器模式,尽管是一种常见的设计模式,但是有点亡羊补牢的感觉。不是首选的设计模式。适配器模式是连接两个类的中间件,当一个类想要调用某一个类的接口时,发现尽管这个类的接口可以实现想要的功能,但是却不能用。比如因为格式的问题等等,这时候需要一个中间件来充当转换器,这就是适配器模式。\r\n 适配器模式:适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。(本段摘自百度百科(因为想找个不是我的白话文的定义-.-))。\r\n 实际生活中,比如我们买美版、欧版的电子产品,人家很多国家时110V电压,而我们国家时220V电压,所以需要一个电源适配器,来转换电压以便能够再国内的电网环境中安全的使用。\r\n 下面的例子是一个假设,由于历史原因或者开发不规范的原因,时有些定义并不一致。比如已经离职的同事A定义了商品字符串为id|num^id|num,而另一个同事B在开发时使用的是id@num#id@num,使得第三个同事C在开发时不能直接调用A和B同事的写好的现成类的方法,需要写一个类做为中间件来转换它。使的他们兼容。开放 - 封闭原则告诉我们,不要去修改人家的类,而是以扩展的方式去改变它。因此适配器模式诞生了。以PHP为代码环境:\r\n[code]\r\n<?php\r\n//id|num^id|num\r\nclass ColleagueA{\r\n public static function getString(){\r\n //读取mysql略\r\n return ''1|1^2|1'';\r\n }\r\n public static function setString($str){\r\n echo ''写进mysql:'' . $str;\r\n }\r\n}\r\n//id@num#id@num\r\nclass ColleagueB{\r\n public static function getString(){\r\n //读取mysql略\r\n return ''1@1#2@1'';\r\n }\r\n public static function setString($str){\r\n echo ''写进mysql:'' . $str;\r\n }\r\n}\r\n//适配器\r\nclass Adaptation{\r\n public static function changeAToB($str){\r\n $data = array();\r\n $arr = explode(''^'', $str);\r\n foreach($arr as $a){\r\n $data[] = explode(''|'', $a);\r\n }\r\n $arr = $data;\r\n $data = '''';\r\n foreach($arr as $a){\r\n $data[] = implode(''@'', $a);\r\n }\r\n return implode(''#'', $data);\r\n }\r\n public static function changeBToA($str){\r\n $data = array();\r\n $arr = explode(''#'', $str);\r\n foreach($arr as $a){\r\n $data[] = explode(''@'', $a);\r\n }\r\n $arr = $data;\r\n $data = '''';\r\n foreach($arr as $a){\r\n $data[] = implode(''|'', $a);\r\n }\r\n return implode(''^'', $data);\r\n }\r\n}\r\n//客户端/接口\r\n$stringFromA = ColleagueA::getString();\r\n$stringFromAdaptation = Adaptation::changeAToB($stringFromA);\r\nColleagueB::setString($stringFromAdaptation);\r\necho ''<br>'';\r\n$stringFromB = ColleagueB::getString();\r\n$stringFromAdaptation = Adaptation::changeBToA($stringFromB);\r\nColleagueA::setString($stringFromAdaptation);\r\n[/code]\r\n\r\nPs:严格执行开发规范,开发前该抽象的抽象,该封装的封装,比到最后没办法了用这个适配器模式强的多。\r\n 适配器模式,一种亡羊补牢的模式,食之无味,弃之可惜。', 1398386259, 12, 9, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(32, 8, '李轩Lane', '面向对象的洗礼:设计模式(十八)之备忘录模式', '备忘录模式,顾名思义,记录某种数据,在需要的时候释放出来。在游戏中,存档,读档就是备忘录模式。被Boss打死后复活,数据回复到打Boss之前,也是设计模式中的备忘录模式。在但是在游戏中,角色类的功能不能带有存储旧状态数据和恢复旧状态数据的方法。把存储和读取的细节封装到一个新类中。职责分离。每个类超过一个功能,就需要考虑拆分了。这也是单一原则的体现。', '备忘录模式_设计模式_设计模式之备忘录模式', '备忘录模式,顾名思义,记录某种数据,在需要的时候释放出来。在游戏中,存档,读档就是备忘录模式。被Boss打死后复活,数据回复到打Boss之前,也是设计模式中的备忘录模式。', '备忘录模式,设计模式,设计模式之备忘录模式', 'PHP|设计模式|备忘录模式', 503, '备忘录模式,顾名思义,记录某种数据,在需要的时候释放出来。在游戏中,存档,读档就是备忘录模式。被Boss打死后复活,数据回复到打Boss之前,也是设计模式中的备忘录模式。在但是在游戏中,角色类的功能不能带有存储旧状态数据和恢复旧状态数据的方法。把存储和读取的细节封装到一个新类中。职责分离。每个类超过一个功能,就需要考虑拆分了。这也是单一原则的体现。\r\n 备忘录模式:在不破坏封装的前提下,捕获一个对象的内部属性数据,将这个状态保存在另一个类中,以便以后的恢复数据需求。\r\n 场景:EA知名游戏模拟人生The Sims,存档和读档。以PHP为代码环境来模拟场景。\r\n[code]\r\n<?php\r\n//管理‘档案器的类’的类\r\nclass Archives{\r\n private $memento;\r\n public function get(){\r\n return $this->memento;\r\n }\r\n public function set($mementoObj){\r\n return $this->memento = $mementoObj;\r\n }\r\n}\r\nclass Memento{\r\n private $mementoData;\r\n public function __construct($data){\r\n $this->mementoData = $data;\r\n }\r\n public function get(){\r\n return $this->mementoData;\r\n }\r\n}\r\nclass GameRole{\r\n private $money;\r\n private $name;\r\n public function setMoney($money){\r\n $this->money = $money;\r\n }\r\n public function setName($name){\r\n $this->name = $name;\r\n }\r\n public function getMoney(){\r\n return $this->money;\r\n }\r\n public function getName(){\r\n return $this->name;\r\n }\r\n public function setMemento(){\r\n $data[''money''] = $this->money;\r\n $data[''name''] = $this->name;\r\n return new Memento($data);\r\n }\r\n public function getMemento($mementoObj){\r\n $mementoData = $mementoObj->get();;\r\n $this->money = $mementoData[''money''];\r\n $this->name = $mementoData[''name''];\r\n }\r\n public function display(){\r\n echo ''玩家'' . $this->name . ''家有家庭资产'' . $this->money . ''元<br>'';\r\n }\r\n}\r\n//客户端/接口\r\n//游戏开始,小明家有100元\r\n$playObj = new GameRole();\r\n$playObj->setName(''小明'');\r\n$playObj->setMoney(100);\r\n$playObj->display();\r\n//存档\r\n$archivesObj = new Archives();\r\n$archivesObj->set($playObj->setMemento());\r\n//小明家做生意亏损了90元\r\n$playObj->setMoney(10);\r\n$playObj->display();\r\n//读档\r\n$playObj->getMemento($archivesObj->get());\r\n$playObj->display();\r\n[/code]\r\n优点:彻底封装了存档和读档的细节实现,完全不对外公开。备忘录类只有玩家类能够操作。对客户端和顶层外网接口完全细节封装和数据封闭。', 1398438254, 8, 10, 0),
(33, 8, '李轩Lane', '面向对象的洗礼:设计模式(十九)之组合模式', '组合模式告诉我们,对待部分和对待整体是一样的。整体和部分就是总部和分部的关系。使用设计模式中的组合模式,客户端不需要知道它调用的到底是整体的接口还是部分的接口。北京总公司为整体,下属有上海分公司,北京总公司财务,北京总公司人事。上海分公司下属有上海分公司财务,上海分公司财务。这就是整体与部分的关系,是组合模式的使用前提。需求中是体现部分和整体的结构时,用户不需要关心是在使用整体的对象还是单个对象而是使用统一的接口对象时,就可以考虑设计模式中的组合模式了。', '组合模式_设计模式_设计模式之组合模式', '组合模式告诉我们,对待部分和对待整体是一样的。整体和部分就是总部和分部的关系。使用设计模式中的组合模式,客户端不需要知道它调用的到底是整体的接口还是部分的接口。', '组合模式,设计模式,设计模式之组合模式', 'PHP|设计模式|组合模式', 647, '组合模式告诉我们,对待部分和对待整体是一样的。整体和部分就是总部和分部的关系。使用设计模式中的组合模式,客户端不需要知道它调用的到底是整体的接口还是部分的接口。北京总公司为整体,下属有上海分公司,北京总公司财务,北京总公司人事。上海分公司下属有上海分公司财务,上海分公司财务。这就是整体与部分的关系,是组合模式的使用前提。需求中是体现部分和整体的结构时,用户不需要关心是在使用整体的对象还是单个对象而是使用统一的接口对象时,就可以考虑设计模式中的组合模式了。\r\n 组合模式:可以用整体-部分的结构来表示一个对象的结构层次。使用组合模式使得用户对组合对象和单个对象没有不同的感受。\r\n 场景:北京总公司,上海分公司,北京财务部,上海财务部的公司结构。以PHP为代码环境。代码仅仅为了说明组合模式,并不推荐实际开发中的使用。\r\n[code]\r\n<?php\r\n//具体的公司\r\nclass ConcreteCompany{\r\n public $name;\r\n private $companyList = array();\r\n public function __construct($name){\r\n $this->name = $name;\r\n }\r\n public function add($obj){\r\n $this->companyList[] = $obj;\r\n }\r\n public function display(){\r\n foreach($this->companyList as $company){\r\n echo ''--'';\r\n echo $company->name;\r\n echo ''<br>'';\r\n if(!empty($company->companyList)){\r\n echo ''--'';\r\n $company->display();\r\n }\r\n }\r\n }\r\n}\r\nclass Department{\r\n public $name;\r\n public function __construct($name){\r\n $this->name = $name;\r\n }\r\n public function add(){\r\n echo ''已经是最小分类了,不能再细分了<br>'';\r\n }\r\n public function display(){\r\n echo ''--'';\r\n echo $this->name;\r\n echo ''<br>'';\r\n }\r\n}\r\n//客户端/接口\r\n$headCompany = new ConcreteCompany(''北京总公司'');\r\n$headCompany->add(new Department(''北京总公司财务部''));\r\n$headCompany->add(new Department(''北京总公司人事部''));\r\n\r\n$concreteCompany1 = new ConcreteCompany(''上海分公司'');\r\n$concreteCompany1->add(new Department(''上海分公司财务部''));\r\n$concreteCompany1->add(new Department(''上海分公司人事部''));\r\n$headCompany->add($concreteCompany1);\r\n\r\n$concreteCompany2 = new ConcreteCompany(''青岛分公司'');\r\n$concreteCompany2->add(new Department(''青岛分公司财务部''));\r\n$concreteCompany2->add(new Department(''青岛分公司人事部''));\r\n$headCompany->add($concreteCompany2);\r\n\r\necho ''全公司组织架构:<br>'';\r\n$headCompany->display();\r\necho ''<br><br>上海分公司组织架构:<br>'';\r\n$concreteCompany1->display();\r\necho ''<br><br>上海分公司组织架构:<br>'';\r\n$concreteCompany2->display();\r\n[/code]', 1398441494, 12, 6, 0),
(34, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十)之迭代器模式', '迭代器模式,将一个列表从头到尾或者从尾到头进行一次遍历。迭代器模式是被提名要求废除的一种设计模式。因为很多的高级语言,如PHP,Python,JAVA等,都已经拥有了foreach。迭代器模式用来访问一个列表的第一个,最后一个,或者某一个的下一个。', '迭代器模式_设计模式_设计模式之迭代器模式', '迭代器模式,将一个列表从头到尾或者从尾到头进行一次遍历。迭代器模式是被提名要求废除的一种设计模式。', '迭代器模式,设计模式,设计模式之迭代器模式', 'PHP|设计模式|迭代器模式', 793, '迭代器模式,将一个列表从头到尾或者从尾到头进行一次遍历。迭代器模式是被提名要求废除的一种设计模式。因为很多的高级语言,如PHP,Python,JAVA等,都已经拥有了foreach。\r\n 迭代器模式:提供一种方法顺序,来访问一个聚合中的各个元素,而不暴露该对象的内部表示。\r\n 迭代器模式用来访问一个列表的第一个,最后一个,或者某一个的下一个。\r\n 以PHP为代码环境模拟一下迭代器模式的思想。抽象一个Iterator类的理由是有可能是正序,有可能倒序查找。\r\n[code]\r\n<?php\r\nabstract class Iterator{\r\n public function first(){\r\n\r\n }\r\n public function last(){\r\n\r\n }\r\n}\r\n//正序\r\nclass PositiveOrder extends Iterator{\r\n private $list;\r\n private $listTmp;\r\n public function __construct($list){\r\n $this->list = $list;\r\n $this->listTmp = array_values($list);\r\n }\r\n public function first(){\r\n return $this->listTmp[0];\r\n }\r\n public function last(){\r\n return $this->listTmp[(count($this->listTmp)-1)];\r\n }\r\n}\r\n//倒序\r\nclass ReverseOrder extends Iterator{\r\n private $list;\r\n private $listTmp;\r\n public function __construct($list){\r\n $this->list = $list;\r\n $this->listTmp = array_values($list);\r\n }\r\n public function first(){\r\n return $this->listTmp[(count($this->listTmp)-1)];\r\n }\r\n public function last(){\r\n return $this->listTmp[0];\r\n }\r\n}\r\n[/code]\r\n 我们还可以去模拟获取某个元素的下一个元素等。\r\n 在PHP中,活跃社区的各个开发者都在为PHP的明天做贡献,为我们提供了已经封装好的函数,用来操作PHP数组的内部指针。\r\n[code]\r\nnext(); 定位指针到当前位置的后一个\r\nprev(); 定位指针到当前位置的前一个\r\nreset(); 重置指针到数组的开始\r\nend(); 定位指针到数组的最后\r\ncurrent(); 取得当前指针位置的值\r\nkey(); 取得当前指针位置的键\r\n[/code]\r\n 使用示例:\r\n[code]\r\n<?php\r\n$arr=array("php"=>"脚本","python"=>"脚本","mysql"=>"数据库");\r\nwhile(list($key,$value)=each($arr)){\r\n echo $key.''============>''.$value.''<br>'';\r\n}\r\n/**\r\n * 输出结果\r\n * php============>脚本\r\n * python============>脚本\r\n * mysql============>数据库\r\n */\r\nreset($arr);\r\nwhile(list($key,$value)=each($arr)){\r\n echo $key.''============>''.$value.''<br>'';\r\n}\r\n/**\r\n * 输出结果\r\n * php============>脚本\r\n * python============>脚本\r\n * mysql============>数据库\r\n */\r\nreset($arr);\r\nnext($arr);\r\nwhile(list($key,$value)=each($arr)){\r\n echo $key.''============>''.$value.''<br>'';\r\n}\r\n/**\r\n * 输出结果\r\n * python============>脚本\r\n * mysql============>数据库\r\n */\r\nend($arr);\r\necho current($arr).''=============>''.key($arr).''<br>'';\r\n/**\r\n * 输出结果\r\n * mysql============>数据库\r\n */\r\nprev($arr);\r\necho current($arr).''=========>''.key($arr).''<br>'';\r\n/**\r\n * 输出结果\r\n * python============>脚本\r\n */\r\n[/code]', 1398498396, 10, 8, 0),
(35, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十一)之单例模式', '单例模式,顾名思义,单个的实例,就是对某个对象,只new一次。单例模式是设计模式常见的一种,用来创建封装好的类的唯一一个实例,这样一来,可以严格控制客户怎么样访问它以及何时访问它,对唯一实例的受控访问。', '单例模式_设计模式_设计模式之单例模式', '单例模式,顾名思义,单个的实例,就是对某个对象,只new一次。单例模式是设计模式常见的一种,用来创建封装好的类的唯一一个实例,这样一来,可以严格控制客户怎么样访问它以及何时访问它,对唯一实例的受控访问。', '单例模式,设计模式,设计模式之单例模式', 'PHP|设计模式|单例模式', 607, '单例模式,顾名思义,单个的实例,就是对某个对象,只new一次。单例模式是设计模式常见的一种,用来创建封装好的类的唯一一个实例,这样一来,可以严格控制客户怎么样访问它以及何时访问它,对唯一实例的受控访问。\r\n 单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点。\r\n 单例模式如何防止一个类被多次new呢?首先,每个类都有一个构造函数,即使没有显式的声明,也是以public存在的,将构造函数设为private。其次,让该类保存实例化后的对象,并提供一个对外的接口。\r\n 示例场景:mysql中user表。以PHP为代码环境,来模拟设计模式中的单例模式。\r\n[code]\r\n<?php\r\nclass UserMysqlModel{\r\n public function get(){\r\n echo ''获取user表的数据<br>'';\r\n }\r\n public function set(){\r\n echo ''写入user表的数据<br>'';\r\n }\r\n public function edit(){\r\n echo ''修改user表的数据<br>'';\r\n }\r\n public function del(){\r\n echo ''删除user表的数据<br>'';\r\n }\r\n}\r\nclass UserBusiness{\r\n private static $userMysqlModelObj = '''';\r\n private function __construct(){\r\n\r\n }\r\n public static function getInstance(){\r\n if(empty(self::$userMysqlModelObj)){\r\n self::$userMysqlModelObj = new UserMysqlModel();\r\n }\r\n return self::$userMysqlModelObj;\r\n }\r\n}\r\n//客户端/接口\r\n$userBusinessObj = UserBusiness::getInstance()->get();\r\n$userBusinessObj = UserBusiness::getInstance()->set();\r\n$userBusinessObj = UserBusiness::getInstance()->edit();\r\n$userBusinessObj = UserBusiness::getInstance()->del();\r\n[/code]', 1398499693, 8, 9, 0),
(36, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十二)之桥接模式', '桥接模式,就是实现系统可能有多角度分类,每一种分类都有可能变化,可能增加或减少。那么,就把这种多角度分离出来让他们独自变化,减少它们之间的耦合。', '桥接模式_设计模式_设计模式之桥接模式', '桥接模式,就是实现系统可能有多角度分类,每一种分类都有可能变化,可能增加或减少。那么,就把这种多角度分离出来让他们独自变化,减少它们之间的耦合。', '桥接模式,设计模式,设计模式之桥接模式', 'PHP|设计模式|桥接模式', 638, '桥接模式,就是实现系统可能有多角度分类,每一种分类都有可能变化,可能增加或减少。那么,就把这种多角度分离出来让他们独自变化,减少它们之间的耦合。\r\n 比如,现在的智能手机,安卓的ipk文件就不能安装在苹果的ios系统上。分类一(按照手机操作系统来分):手机系统分为安卓和IOS,安卓的软件分为游戏、音乐等,IOS的软件也分为游戏、音乐等。分类二(按照软件来分):软件分为游戏和音乐,游戏分为安卓游戏和IOS游戏,音乐也分为安卓音乐和IOS音乐。\r\n 思考:增加一个手机系统,如Windos 8。那么,分类就变了。如下:分类一(按照手机操作系统来分):手机系统分为安卓和IOS和Windows 8,安卓的软件分为游戏、音乐等,IOS的软件也分为游戏、音乐等,Windows 8的软件也分为游戏和音乐等。分类二(按照软件来分):软件分为游戏和音乐,游戏分为安卓游戏和IOS游戏和Windows 8游戏,音乐也分为安卓音乐和IOS音乐和Windows 8音乐。\r\n 这种分类的弊病非常明显,如果要增加一个手机的操作系统,相应的需要改变原有分类,增加大量的class文件。操作系统和手机软件的分类是高强度耦合。\r\n 在面向对象的变成里,耦合度越高的越不利于复用,设计模式的重点就是降低耦合,减少复制-粘贴的编码模式。在这个例子中,引出了一种设计模式,叫做桥接模式。\r\n 桥接模式:将抽象的部分与它的实现分离,使他们可以独立变化。\r\n 所谓的实现,就是抽象类和派生类用来实现自己的对象。白话文就是:就是实现系统可能有多角度分类,每一种分类都有可能变化,可能增加或减少。那么,就把这种多角度分离出来让他们独自变化,减少它们之间的耦合。\r\n 桥接模式的分类:手机操作系统分为安卓和IOS,手机软件分为游戏和音乐。操作系统和软件相互独立,没有强的明显关系,操作系统和手机软件不进行分类关联,它们俩的真实关系请继续往后看。\r\n 那么,耦合度降低了,增加windows8只需要在操作系统分类下增加一个windows8,软件则不需要变化。\r\n 再引入一个原则,合成-聚合复用原则:尽量使用合成-聚合,尽量不要使用类的继承。\r\n 聚合是弱拥有关系,A可以包含B,但是B不是A的一部分。合成是强拥有关系,严格的部分和整体,两者拥有相同的生命周期。\r\n 大雁的翅膀和大雁本身是合成关系,生命周期一样,是强拥有关系。大雁和雁群是聚合关系,大雁只属于一个雁群,但是雁群不仅仅只有这一个大雁。(本例摘自《大话设计模式》)\r\n 回到上一个话题,在桥接模式的分类下,手机操作系统和手机软件之间的关系,就是聚合关系,弱的关系,才可以降低耦合。\r\n 以PHP为代码环境,说明手机的例子。\r\n[code]\r\n<?php\r\nabstract class Soft{\r\n public function run(){}\r\n}\r\nclass Game extends Soft{\r\n public function run(){\r\n echo ''手机游戏正在运行...<br>'';\r\n }\r\n}\r\nclass Mp3 extends Soft{\r\n public function run(){\r\n echo ''手机音乐播放器正在运行...<br>'';\r\n }\r\n}\r\nabstract class OS{\r\n protected $softObj;\r\n public function setSoftObj($softObj){\r\n $this->softObj = $softObj;\r\n }\r\n public function run(){}\r\n}\r\nclass Ios extends OS{\r\n public function run(){\r\n $this->softObj->run();\r\n }\r\n}\r\nclass Android extends OS{\r\n public function run(){\r\n $this->softObj->run();\r\n }\r\n}\r\n//客户端/接口\r\necho ''购买了Iphone一台,搭载IOS操作系统<br>'';\r\n$iphone = new Ios();\r\n$iphone->setSoftObj(new Game());\r\n$iphone->run();\r\n$iphone->setSoftObj(new Mp3());\r\n$iphone->run();\r\n\r\necho ''购买了三星一台,搭载安卓操作系统<br>'';\r\n$samsung = new Ios();\r\n$samsung->setSoftObj(new Game());\r\n$samsung->run();\r\n$samsung->setSoftObj(new Mp3());\r\n$samsung->run();\r\n[/code]', 1398579192, 10, 12, 0),
(37, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十三)之命令模式', '命令模式解决了行为者与请求者过于紧耦合。即设计模式之命令模式将一个请求指定一个响应者的模式进行了解耦化。命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,请求排队或记录日志已经执行可撤销的操作。', '命令模式_设计模式_设计模式之命令模式', '命令模式解决了行为者与请求者过于紧耦合。即设计模式之命令模式将一个请求指定一个响应者的模式进行了解耦化。', '命令模式,设计模式,设计模式之命令模式', 'PHP|设计模式|命令模式', 595, '命令模式解决了行为者与请求者过于紧耦合。即命令模式将一个请求指定一个响应者的模式进行了解耦化。\r\n 命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,请求排队或记录日志已经执行可撤销的操作。\r\n 命令模式的优点:第一、比较容易的设计一个队列;第二、比较容易的命令写入日志;第三、允许接收请求的一方决定是否要否决请求;第四、比较容易的实现请求的撤销和重做;第五、增加新命令不影响其他类;第六、把请求者和响应者分离。\r\n 场景:淘宝下订单,紧耦合就是点击下单按钮,请求直接发送给响应者。使用设计模式的命令模式进行解耦操作,就是点击下订单,请求发送给一个中央订单处理系统,然后由中央订单处理系统这个中间件分发给淘宝订单中心、天猫订单中心、聚划算订单中心等不同的操作,同时添加日志记录等。以PHP为代码环境来说明命令模式。\r\n[code]\r\n<?php\r\nclass OrderCenter{\r\n public $orderList;\r\n public function setOrder($order){\r\n //没有库存\r\n $isStock = true;\r\n //秒杀活动没有开始\r\n $isActivityTime = true;\r\n if(!$isStock && !$isActivityTime){\r\n exit(''活动没开始,即将跳转回之前的页面'');\r\n }else{\r\n $this->orderList[] = $order;\r\n }\r\n }\r\n public function cancelOrder(){\r\n\r\n }\r\n //假定是一个队列服务\r\n public function queue(){\r\n foreach($this->orderList as $key=>$order){\r\n echo ''订单处理成功<br>'';\r\n $order->setOrder();\r\n unset($this->orderList[$key]);\r\n }\r\n }\r\n}\r\nclass Order{\r\n public function buyClothes(){\r\n echo ''购买衣服成功<br>'';\r\n }\r\n public function buyShoes(){\r\n echo ''购买鞋子成功<br>'';\r\n }\r\n}\r\nabstract class Command{\r\n public $request;\r\n public function __constrcut($requestObj){\r\n $this->request = $requestObj;\r\n }\r\n public function setOrder(){}\r\n}\r\nclass TaoBao extends Command{\r\n public function __construct($requestObj){\r\n parent::__constrcut($requestObj);\r\n }\r\n public function setOrder(){\r\n echo ''淘宝订单:<br>'';\r\n $this->request->buyClothes();\r\n $this->request->buyShoes();\r\n }\r\n}\r\nclass Tmall extends Command{\r\n public function __construct($requestObj){\r\n parent::__constrcut($requestObj);\r\n }\r\n public function setOrder(){\r\n echo ''天猫订单:<br>'';\r\n $this->request->buyClothes();\r\n $this->request->buyShoes();\r\n }\r\n}\r\n//客户端/接口\r\n$orderObj = new Order();\r\n$taobaoObj = new TaoBao($orderObj);\r\n$tmallObj = new Tmall($orderObj);\r\n$orderCenterObj = new OrderCenter();\r\n$orderCenterObj->setOrder($taobaoObj);\r\n$orderCenterObj->setOrder($tmallObj);\r\n$orderCenterObj->queue();\r\n[/code]', 1398667590, 11, 10, 0),
(38, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十四)之职责链模式', '职责链模式解决了请求需要经过大量的臃肿的逻辑判断,设计模式的职责链模式采用了层层上报的方式,请求发送给响应方,响应方1若不能处理,则发送给响应2,响应2若不能处理则发送给响应3...直到处理为止。职责链模式的关键是,当客户提交一个请求时,请求是沿着一个链条进行传递,直到有一个对象可以负责这个请求为止。', '职责链模式_设计模式_设计模式之职责链模式', '职责链模式解决了请求需要经过大量的臃肿的逻辑判断,设计模式的职责链模式采用了层层上报的方式,请求发送给响应方,响应方1若不能处理,则发送给响应2,响应2若不能处理则发送给响应3,直到处理为止。', '职责链模式,设计模式,设计模式之职责链模式', 'PHP|设计模式|职责链模式', 646, '职责链模式解决了请求需要经过大量的臃肿的逻辑判断,设计模式的职责链模式采用了层层上报的方式,请求发送给响应方,响应方1若不能处理,则发送给响应2,响应2若不能处理则发送给响应3...直到处理为止。职责链模式的关键是,当客户提交一个请求时,请求是沿着一个链条进行传递,直到有一个对象可以负责这个请求为止。\r\n 职责链模式:使多个对象都有机会处理请求,从而避免了请求者和响应者的耦合关系,将响应者连成一个链条,层层传递,沿着这个链条传递请求,直到可以有一个对象处理为止。\r\n 职责链的好处:请求者和响应者都没有对方的明确信息,链中的对象也不知道链的结构,结果是职责模式中的职责链可以简化对象的相互连接,降低耦合,他们只需要保存一个继承者,而不需要保存所有的继承者。在增加和修改一个请求的结构时,更加灵活。\r\n 场景:请假,组长只能批1天的请假,技术总监可以批3天的请假,3天以上需要老板亲自批示。以PHP为代码环境来描述职责链模式。\r\n \r\n[code]\r\n<?php\r\nabstract class Manager{\r\n protected $position;\r\n protected $lead;\r\n public function getPosition(){\r\n return $this->position;\r\n }\r\n public function setPosition($position){\r\n $this->position = $position;\r\n }\r\n public function setLead($leadObj){\r\n $this->lead = $leadObj;\r\n }\r\n}\r\nclass GroupLeader extends Manager{\r\n public function __construct($position){\r\n $this->position = $position;\r\n }\r\n public function response($day){\r\n if($day == 1){\r\n echo $this->position . ''批准<br>'';\r\n }else{\r\n echo $this->position . ''无权处理,请示上级<br>'';\r\n $this->lead->response($day);\r\n }\r\n }\r\n}\r\nclass Director extends Manager{\r\n public function __construct($position){\r\n $this->position = $position;\r\n }\r\n public function response($day){\r\n if($day > 1 && $day <= 3){\r\n echo $this->position . ''批准<br>'';\r\n }else{\r\n echo $this->position . ''无权处理,请示上级<br>'';\r\n $this->lead->response($day);\r\n }\r\n }\r\n}\r\nclass CEO extends Manager{\r\n public function __construct($position){\r\n $this->position = $position;\r\n }\r\n public function response($day){\r\n if($day > 3){\r\n echo $this->position . ''批准<br>'';\r\n }else{\r\n echo $this->position . ''无权处理,请示上级<br>'';\r\n $this->lead->response($day);\r\n }\r\n }\r\n}\r\n//客户端/接口\r\n$groupLead = new GroupLeader(''组长'');\r\n$director = new Director (''总监'');\r\n$ceo = new CEO(''首席执行官'');\r\n$groupLead->setLead($director);\r\n$director->setLead($ceo);\r\n$dayArr = array(1, 2, 3, 4, 5);\r\nforeach($dayArr as $day){\r\n echo ''请假'' . $day . ''天结果<br>'';\r\n $groupLead->response($day);\r\n echo ''<br><br><br>'';\r\n}\r\n[/code]', 1398749898, 8, 14, 0),
(39, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十五)之中介模式', '中介模式,是非常非常常见的一种设计模式。一般应用于一组对象以定义良好但是复杂的方式进行通信的场合。定制一个分布在多个类中的行为,而不像生成太多的子类。这是中介模式的应用场景。中介类的集中化控制即是中介模式的优点,又是中介模式的缺点。', '中介模式_设计模式_设计模式之中介模式', '中介模式,是非常非常常见的一种设计模式。一般应用于一组对象以定义良好但是复杂的方式进行通信的场合。定制一个分布在多个类中的行为,而不像生成太多的子类。这是中介模式的应用场景。', '中介模式,设计模式,设计模式之中介模式', 'PHP|设计模式|中介模式', 612, '中介模式,是非常非常常见的一种设计模式。一般应用于一组对象以定义良好但是复杂的方式进行通信的场合。定制一个分布在多个类中的行为,而不像生成太多的子类。这是中介模式的应用场景。\r\n 中介模式的优点:中介类的集中化控制。\r\n 中介模式的缺点:中介类的集中化控制。\r\n 中介模式的中介类,集中化控制了所有的对象,减少了请求者和响应者的耦合。把中介封装在一个对象中,注意力从关注对象本身变成了关注它们之间的交互,更加宏观。缺点也就显而易见,因为集中化的控制,使得中介类越发的庞大,不易维护。\r\n 场景:中介抽象类联合国,中介具体类安理会。成员抽象类为国家,成员具体类为美国和伊拉克。以PHP为代码环境来描述中介模式。\r\n \r\n[code]\r\n<?php\r\n<?php\r\nabstract class UN{\r\n public function sentMessage($message, $countryObj){}\r\n}\r\nabstract class Country{\r\n protected $UNObj;\r\n public function __construct($UNObj){\r\n $this->UNObj = $UNObj;\r\n }\r\n}\r\nclass US extends Country{\r\n public function __construct($UNObj){\r\n parent::__construct($UNObj);\r\n }\r\n public function sentMessage($message){\r\n $this->UNObj->sentMessage($message, $this);\r\n }\r\n public function getMessage($message){\r\n echo ''美国收到某国消息:'' . $message . ''<br>'';\r\n }\r\n}\r\nclass Iraq extends Country{\r\n public function __construct($UNObj){\r\n parent::__construct($UNObj);\r\n }\r\n public function sentMessage($message){\r\n $this->UNObj->sentMessage($message, $this);\r\n }\r\n public function getMessage($message){\r\n echo ''伊拉克收到某国消息:'' . $message . ''<br>'';\r\n }\r\n}\r\nclass UNSC extends UN{\r\n private $usObj;\r\n private $iraqObj;\r\n public function setUsObj($obj){\r\n $this->usObj = $obj;\r\n }\r\n public function setIraqObj($obj){\r\n $this->iraqObj = $obj;\r\n }\r\n public function sentMessage($message, $sentObj){\r\n if($this->usObj == $sentObj){\r\n $this->iraqObj->getMessage($message);\r\n }else{\r\n $this->usObj->getMessage($message);\r\n }\r\n }\r\n}\r\n//客户端/接口\r\n$unsc = new UNSC();\r\n$us = new US($unsc);\r\n$iraq = new Iraq($unsc);\r\n$unsc ->setUsObj($us);\r\n$unsc->setIraqObj($iraq);\r\n$us->sentMessage(''你们不可以研发核武器!'');\r\n$iraq->sentMessage(''我们没有和武器!'');\r\n[/code]', 1398845807, 8, 7, 0),
(40, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十六)之享元模式', '享元模式解决了大量几乎相似的对象的这种情况。设计模式中的享元模式使程序运行时更加节省服务器资源。享元模式是一种非常好的设计模式。如果一个应用程序使用了大量的对象,而大量的这些对象对服务器资源造成了很大的开销和压力时,就应该考虑使用享元模式。', '享元模式_设计模式_设计模式之享元模式', '享元模式解决了大量几乎相似的对象的这种情况。设计模式中的享元模式使程序运行时更加节省服务器资源。享元模式是一种非常好的设计模式。', '享元模式,设计模式,设计模式之享元模式', 'PHP|设计模式|享元模式', 552, '享元模式解决了大量几乎相似的对象的这种情况。设计模式中的享元模式使程序运行时更加节省服务器资源。享元模式是一种非常好的设计模式。如果一个应用程序使用了大量的对象,而大量的这些对象对服务器资源造成了很大的开销和压力时,就应该考虑使用享元模式。\r\n 享元模式:运用共享技术有效的支持大量的细粒度对象。\r\n 比如:围棋只有黑白两种棋子,用一个对象生成黑棋子,一个对象生成白棋子,是要一份代码共享给所有的黑棋子共同使用呢,还是每个黑棋子独立一个对象。这就是享元模式,共享对象以达到节省开销的目的。\r\n 场景:阿里云旗下的万网提供快速建站的服务,它是给每个用户独立生成一个网站所有的源代码,还是说同类型的网站共享一份代码?答案是后者(示例仅为说明享元模式,并不代表万网的真实实现方式)。以PHP为代码环境,模拟设计模式之享元模式的代码实现。\r\n[code]\r\n<?php\r\nclass User{\r\n private $name;\r\n public function __construct($name){\r\n $this->setName($name);\r\n }\r\n public function setName($name){\r\n $this->name = $name;\r\n }\r\n public function getName(){\r\n return $this->name;\r\n }\r\n}\r\nabstract class Website{\r\n private $name;\r\n public function __construct($name){\r\n $this->setName($name);\r\n }\r\n public function setName($name){\r\n $this->name = $name;\r\n }\r\n public function getName(){\r\n return $this->name;\r\n }\r\n}\r\nclass ConcreteWebsite extends Website{\r\n public function __construct($name){\r\n parent::__construct($name);\r\n }\r\n public function useWebsite($userObj){\r\n echo ''网站名称:'' . $this->getName() . ''。 所属用户'' . $userObj->getName() . ''<br>'';\r\n }\r\n}\r\nclass WebsiteFactory{\r\n private $userWebsiteList = array();\r\n public function getWebsite($key, $name){\r\n if(!isset($this->userWebsiteList[$key])){\r\n $this->userWebsiteList[$key] = new ConcreteWebsite($name);\r\n }\r\n return $this->userWebsiteList[$key];\r\n }\r\n}\r\n//客户端/接口\r\n//网站工厂\r\n$websiteFactory = new WebsiteFactory();\r\n\r\n//采用万网提供的第一套模板并起名\r\n$website = $websiteFactory->getWebsite(''1'', ''LaneBlog'');\r\n$website->useWebsite(new User(''小轩''));\r\n\r\n//采用万网提供的第一套模板并起名\r\n$website = $websiteFactory->getWebsite(''1'', ''Lane博客'');\r\n$website->useWebsite(new User(''小明''));\r\n\r\n//采用万网提供的第一套模板并起名\r\n$website = $websiteFactory->getWebsite(''1'', ''LixuanBlog'');\r\n$website->useWebsite(new User(''小红''));\r\n\r\n//采用万网提供的第二套模板并起名\r\n$website = $websiteFactory->getWebsite(''2'', ''论坛'');\r\n$website->useWebsite(new User(''小白''));\r\n[/code]\r\n 根据结果可以看到,多个用户,前三个用户使用的是同一套系统。节省开销。至于名称,从库里读出来即可。这里完全不需要。', 1399008324, 16, 17, 0),
(41, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十七)之解释器模式', '解释器模式,作为PHPer应该非常非常非常熟悉的一种,尽管不知道它叫做解释器模式,但是肯定使用过它。在解释器模式的最佳应用,就是大量优秀的模板引擎。解释器模式解决了一种特定的类型的问题发生的频率足够高,那么就可能值得将该问题的各个势力表述为一个简单的语言中的句子。这就构建了一个解释器,解释器他哦各国解释这些句子来解决问题。', '解释器模式_设计模式_设计模式之解释器模式', '解释器模式,作为PHPer应该非常非常非常熟悉的一种,尽管不知道它叫做解释器模式,但是肯定使用过它。在解释器模式的最佳应用,就是大量优秀的模板引擎。', '解释器模式,设计模式,设计模式之解释器模式', 'PHP|设计模式|解释器模式', 925, '解释器模式,作为PHPer应该非常非常非常熟悉的一种,尽管不知道它叫做解释器模式,但是肯定使用过它。在解释器模式的最佳应用,就是大量优秀的模板引擎。解释器模式解决了一种特定的类型的问题发生的频率足够高,那么就可能值得将该问题的各个势力表述为一个简单的语言中的句子。这就构建了一个解释器,解释器他哦各国解释这些句子来解决问题。\r\n 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。\r\n 比如:模板引擎smart;比如论坛的UBB代码,就是用[url=http://www.lanecn.com]LaneBlog[/url]来表示<a href="http://www.lanecn.com/>LaneBlog</a>;还比如正则表达式等\r\n 场景:a表示你,b表示好,c表示世界。1表示我说,2表示你说。以PHP为代码环境,模拟设计模式之解释器模式的代码实现。\r\n[code]\r\n<?php\r\nclass Content{\r\n private $content = '''';\r\n public function get(){\r\n return $this->content;\r\n }\r\n public function set($content){\r\n $this->content = $content;\r\n }\r\n}\r\nclass Expression{\r\n public function interpret($contentObj){\r\n $content = $contentObj->get();\r\n if(!empty($content)){\r\n $lenth = strlen($content);\r\n for($i=0; $i<$lenth; $i++){\r\n if(is_numeric($content[$i])){\r\n Number::excute($content[$i]);\r\n }else if(is_string($content[$i])){\r\n String::excute($content[$i]);\r\n }\r\n }\r\n }\r\n }\r\n}\r\nclass Number{\r\n public static function excute($value){\r\n $data = '''';\r\n switch($value){\r\n case 1:\r\n $data = ''我说:'';\r\n break;\r\n case 2:\r\n $data = ''你说:'';\r\n break;\r\n default:\r\n break;\r\n }\r\n echo $data;\r\n }\r\n}\r\n//a表示你,b表示好,c表示世界。1表示我说,2表示你说\r\nclass String{\r\n public static function excute($value){\r\n $data = '''';\r\n switch($value){\r\n case ''a'':\r\n $data = ''你'';\r\n break;\r\n case ''b'':\r\n $data = ''好'';\r\n break;\r\n case ''c'':\r\n $data = ''世界'';\r\n break;\r\n default:\r\n break;\r\n }\r\n echo $data;\r\n }\r\n}\r\n//客户端/接口\r\n$contentObj = new Content();\r\n$str = ''1abc'';\r\n$contentObj->set($str);\r\necho ''解密'' . $str . '':<br>'';\r\n$expression = new Expression();\r\n$expression->interpret($contentObj);\r\necho ''<br>'';\r\n$str = ''2abc'';\r\n$contentObj->set($str);\r\necho ''解密'' . $str . '':<br>'';\r\n$expression = new Expression();\r\n$expression->interpret($contentObj);\r\n[/code]', 1399089833, 14, 11, 0),
(42, 8, '李轩Lane', '面向对象的洗礼:设计模式(二十八)之访问者模式', '访问者模式,是设计模式中最难的一种模式。访问者模式适用于数据结构相对稳定的系统。访问者模式对数据结构和作用于结构上的操作之间进行了一次解耦合。访问者模式的目的是把处理从数据结构分离出来。访问者模式的适用场景:所开发的系统具有比较问题的数据结构,又有抑郁变化的算法。', '访问者模式_设计模式_设计模式之访问者模式', '访问者模式,是设计模式中最难的一种模式。访问者模式适用于数据结构相对稳定的系统。访问者模式对数据结构和作用于结构上的操作之间进行了一次解耦合。', '访问者模式,设计模式,设计模式之访问者模式', 'PHP|设计模式|访问者模式', 599, '访问者模式,是设计模式中最难的一种模式。访问者模式适用于数据结构相对稳定的系统。访问者模式对数据结构和作用于结构上的操作之间进行了一次解耦合。访问者模式的目的是把处理从数据结构分离出来。\r\n 访问者模式的适用场景:所开发的系统具有比较问题的数据结构,又有抑郁变化的算法。因为访问者模式使得算法操作的增加和扩展变得容易。优点是增加新的操作更加容易,因为增加新操作就是意味着增加新的访问者,访问者模式将有关的行为集中到一个对象中。缺点显而易见了,就是改变数据结构变得下个对困难。\r\n 访问者模式:表示一个作用于某对象的结构中的各个元素的操作。它使你可以在不改变各元素的前提下定义作用于这些元素的新操作。\r\n 场景:人类分为男女,对于人类这个系统,分类是非常固定的,一个元素是男,一个元素是女(人妖滚粗)。男女对同一件事情往往有不同的观点。以PHP为代码环境,模拟设计模式之访问者模式的代码实现。(暂时没有想到好的例子,就从《大话设计模式》中访问者模式摘了一段)\r\n[code]\r\n<?php\r\nclass Action{\r\n public function getManView(Man $manObj){}\r\n public function getWomanView(Woman $manObj){}\r\n}\r\nclass Person{\r\n public function accept(Action $actionObj){}\r\n}\r\nclass Success extends Action{\r\n public function getManView(Man $manObj){\r\n echo sprintf(''%s成功时,背后多半有一个伟大的女人<br>'', $manObj->getName());\r\n }\r\n public function getWomanView(Woman $womanObj){\r\n echo sprintf(''%s成功时,背后多半有一个不成功的女人<br>'', $womanObj->getName());\r\n }\r\n}\r\nclass Failing extends Action{\r\n public function getManView(Man $manObj){\r\n echo sprintf(''%s失败时,闷头喝酒,谁也不用劝<br>'', $manObj->getName());\r\n }\r\n public function getWomanView(Woman $womanObj){\r\n echo sprintf(''%s失败时,眼泪汪汪,谁也劝不住<br>'', $womanObj->getName());\r\n }\r\n}\r\nclass Love extends Action{\r\n public function getManView(Man $manObj){\r\n echo sprintf(''%s恋爱时,凡事不懂也要装懂<br>'', $manObj->getName());\r\n }\r\n public function getWomanView(Woman $womanObj){\r\n echo sprintf(''%s恋爱时,凡事懂也要装不懂<br>'', $womanObj->getName());\r\n }\r\n}\r\nclass Man extends Person{\r\n private $name = ''男人'';\r\n public function getName(){\r\n return $this->name;\r\n }\r\n public function accept(Action $actionObj){\r\n $actionObj->getManView($this);\r\n }\r\n}\r\nclass Woman extends Person{\r\n private $name = ''女人'';\r\n public function getName(){\r\n return $this->name;\r\n }\r\n public function accept(Action $actionObj){\r\n $actionObj->getWomanView($this);\r\n }\r\n}\r\nclass ObjectStructure{\r\n private $elementList;\r\n public function add(Person $elementObj){\r\n $this->elementList[] = $elementObj;\r\n }\r\n public function display(Action $visitorObj){\r\n foreach($this->elementList as $element){\r\n $element->accept($visitorObj);\r\n }\r\n }\r\n}\r\n//客户端/接口\r\n$o = new ObjectStructure();\r\n$o->add(new Man());\r\n$o->add(new Woman());\r\n\r\n$successObj = new Success();\r\n$o->display($successObj);\r\n\r\n$failingObj = new Failing();\r\n$o->display($failingObj);\r\n\r\n$loveObj = new Love();\r\n$o->display($loveObj);\r\n[/code]\r\n 这里用到一个双分派技术。客户端将状态(成功、失败、恋爱)作为参数传递给男人,这是第一次分派。男人类调用作为参数的“具体状态中的方法-男人的观点”,同时将自身传递给状态的对象,这是第二次分派。双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型。双分派的好处是,如果要增加结婚类,只需要增加如下:\r\n[code]\r\nclass Marry extends Action{\r\n public function getManView(Man $manObj){\r\n echo sprintf(''%s结婚时,有妻徒刑<br>'', $manObj->getName());\r\n }\r\n public function getWomanView(Woman $womanObj){\r\n echo sprintf(''%s结婚时,婚姻保险<br>'', $womanObj->getName());\r\n }\r\n}\r\n[/code]\r\n除此之外,客户端在需要的时候调用即可。不需要动其他的代码,增加新算法,只需要扩展一个新类。完美体现开放-封闭原则。', 1399214709, 14, 10, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(43, 13, '李轩Lane', 'Python学习第一阶段:Python的电话本', 'Python学习第一阶段:Python的电话本。涉及了Python的基础内容,数据类型中的字典,类,存储器等。对电话本进行查询,增加,删除,修改的功能。', 'Python示例_用Python实现电话本逻辑。', 'Python学习第一阶段:Python的电话本。涉及了Python的基础内容,数据类型中的字典,类,存储器等。', 'Python示例,用Python实现电话本逻辑。', 'Python|Python基础|Python示例', 3236, '<p>本例是Python基础示例。涉及Python基础,包括语法、字典型数据结构、类、引入库、pickle实现的存储器、异常处理等。\r\n 示例是一个电话本。可以对电话本进行增加、删除、修改、获取列表和获取单人的。\r\n Python中,Pickle和cPickle都可以完成存储器的任务,不过cPickle是C语言所写,据称性能高于Pickle1000倍\r\n Python中的Pickle是把一个对象存入文件中。作为完全面向对象的语言,在声明/初始化一个变量的时候,比如字典,也就是关联数组,Python其实是在实例化一个字典对象。那么Pickle就可以把这个字典对象存入一个文件,读出来的时候不但这个字典是完整的数据,而且可以继续使用这个字典对象的方法。\r\n Python是用缩进来时别语句块的。因为我是在VIM下写好复制出来的,所以在博客看到的可能缩进会有问题。\r\n[code]\r\n#引入pickle库。cPickle比Pickle快1000倍\r\nimport cPickle as pickle\r\n#import Pickle as pickle\r\n\r\n#电话本类\r\nclass Address:\r\n #初始化\r\n def __init__(self):\r\n #把数据存到那个文件里\r\n self.filename = 'list.data'\r\n f = file(self.filename)\r\n #如果文件是新建的或者是空内容的,则初始化为一个空的字典(关联数组)\r\n try:\r\n self.lists = pickle.load(f)\r\n except:\r\n print 'Address Book is empty.initializing.....'\r\n self.lists = {}\r\n f.close()\r\n #添加联系人\r\n def add(self, name, age, mobile, mail):\r\n newUser = {'name':name, 'age':age, 'mobile':mobile, 'mail':mail}\r\n self.lists[name] = newUser\r\n #删除联系人\r\n def delete(self, name):\r\n if name in self.lists:\r\n del self.lists[name]\r\n print 'delete ', name\r\n else:\r\n print 'No exists ', name\r\n #获取列表\r\n def getList(self):\r\n print 'Address Book List:'\r\n print self.lists\r\n #获取指定姓名的联系人\r\n def getOne(self, name):\r\n if name in self.lists:\r\n print self.lists[name]\r\n else:\r\n print 'Not Exists:', name\r\n #修改联系人\r\n def edit(self, name, key, value):\r\n self.lists[name][key] = value\r\n #类运行结束,执行特殊方法__del__,也就是析构函数\r\n def __del__(self):\r\n f = file(self.filename, 'w')\r\n pickle.dump(self.lists, f)\r\n f.close()\r\n\r\n#初始化电话本类\r\nobj = Address()\r\n#添加一个联系人\r\nobj.add('lane', 23, 18500000000, '[email protected]')\r\n#获取所有联系人的列表\r\nobj.getList()\r\n#获取lane这个人的联系方式\r\nobj.getOne('lane')\r\n#获取xiaoming这个人的联系方式\r\nobj.getOne('xiaoming')\r\n//修改lane这个人的年龄为24\r\nobj.edit('lane', 'age', '24')\r\n[/code]</p>', 1399544003, 31, 18, 0),
(44, 10, '李轩Lane', 'Redis数据结构详解,五种数据结构分分钟掌握', 'redis数据类型共有五种,介绍redis数据类型和redis数据命令。redis数据结构分为字符串类型、散列类型、列表类型、集合类型、有序集合类型。来看看如何使用redis的数据类型和redis数据命令吧。', 'redis数据类型_redis数据命令_redis数据结构', 'redis数据类型共有五种,介绍redis数据类型和redis数据命令。redis数据结构分为字符串类型、散列类型、列表类型、集合类型、有序集合类型。来看看如何使用redis的数据类型和redis数据命令吧。', 'redis数据类型,redis数据命令,redis数据结构', 'redis|redis数据类型|redis数据命令', 5844, 'redis数据类型分为:字符串类型、散列类型、列表类型、集合类型、有序集合类型。\r\n redis这么火,它运行有多块?一台普通的笔记本电脑,可以在1秒钟内完成十万次的读写操作。\r\n 原子操作:最小的操作单位,不能继续拆分。即最小的执行单位,不会被其他命令插入。高并发下不存在竞态条件。\r\n KEY的命名:一个良好的建议是article:1:title来存储ID为1的文章的标题。\r\n 一、前言。\r\n 1、获取key的列表:KEYS pattern 通配符有?*[]和转义\\\r\n 2、key是否存在: EXISTS key 存在返回1,不存在返回0.\r\n 3、建立key和删除key:SET key 和 DEL key\r\n 4、根据key获取该键所存储的redis数据类型:TYPE key。返回是string、list、hash、set、zset。下面会对这5种返回的redis数据类型逐一讲解。\r\n 5、rename oldkey newkey:对key重命名,如果newkey存在则覆盖。\r\n 6、renamenx oldkey newkey:对key重命名,如果newkey存在则不覆盖。\r\n 7、randomkey:随即返回一个key\r\n 8、move key db-index:将key移动到指定的数据库中,如果key不存在或者已经在该数据库中,则返回0。成功则返回1.\r\n\r\n\r\n 二、Redis数据类型 Redis数据命令\r\n 1、Redis数据类型一字符串类型:\r\n 这个很好理解,一个key存储一个字符串。如果你要存数据呢?转换成Json或者其他的字符串序列化。\r\n\r\n 2、Redis数据命令一字符串类型:\r\n 1)赋值:SET key value。如set hello world\r\n 2)取值:GET key。如get hello。返回是world\r\n 3)自增:INCR key。就是Mysql的AUTO_INCREMENT。每次执行INCR key时,该key的值都会+1.若key不存在,则先建立一个0,然后+1,返回1。如果值不是整数则报错。该操作是原子操作。\r\n 4)自减:DECR key。将指定key的值减少1.如DECR num,就是num-1\r\n 5)自增N:INCRBY key increment用来给指定key的值加increment。如INCRBY num 5就是num+5\r\n 6)自减N:DECRBY key increment用来给指定key的值减increment。如DECRBY num 5就是num-5\r\n 7)增加浮点数:INCRBYFLOAT key increment。\r\n 8)向尾部追加:APPEND key value。如set test:key 123 append test:key 456 get test:key就是123456\r\n 9)获取长度:STRLEN key。\r\n 10)同时给多个key 赋值:MSET title 这是标题 description 这是描述 content 这是内容。\r\n 11)同时获取多个key的值:MGET title description content\r\n 12)位操作之获取:GETBIT key offset。如字符a在redis中的存储为01100001(ASCII为98),那么GETBIT key 2就是1,GET key 0就是0。\r\n 13)位操作之设置:SETBIT key offset value。如字符a在redis中的存储为01100001(ASCII为98),那么SETBIT key 6 0,SETBIT key 5 1那么get key得到的是b。因为取出的二进制为01100010。\r\n 14)位操作之统计:BITCOUNT key [start] [end]:BITCOUNT key用来获取key的值中二进制是1的个数。而BITCOUNT key start end则是用来统计key的值中在第start和end之间的子字符串的二进制是1的个数(好绕啊)。\r\n 15)位操作之位运算:BITOP operation resultKey key1 key2。operation是位运算的操作,有AND,OR,XOR,NOT。resultKey是把运算结构存储在这个key中,key1和key2是参与运算的key,参与运算的key可以指定多个。\r\n\r\n 3、Redis数据类型二散列类型:\r\n\r\n Redis是以字典(关联数组)的形式存储的,一个key对应一个value。在字符串类型中,value只能是一个字符串。那么在散列类型,也叫哈希类型中,value对应的也是一个字典(关联数组)。那么就可以理解,Redis的哈希类型/散列类型中,key对应的value是一个二维数组。但是字段的值只可以是字符串。也就是说只能是二维数组,不能有更多的维度。\r\n\r\n 4、Redis数据命令二散列类型:\r\n 1)赋值:HSET key field value。如hset user name lane。hset user age 23\r\n 2)取值:HGET key field。如hget user name,得到的是lane。\r\n 3)同一个key多个字段赋值:HMSET key field1 value1 field2 value2...\r\n 4)同一个KEY多个字段取值:HMGET key field1 fields2...\r\n 5)获取KEY的所有字段和所有值:HGETALL key。如HGETALL user得到的是name lane age 23。每个返回都是独立的一行。\r\n 6)字段是否存在:HEXISTS key field。存在返回1,不存在返回0\r\n 7)当字段不存在时赋值:HSETNX key field value。如果key下面的字段field不存在,则建立field字段,且值为value。如果field字段存在,则不执行任何操作。它的效果等于HEXISTS + HSET。但是这个命令的优点是原子操作。再高的并发也不会怕怕。\r\n 8)自增N:HINCREBY key field increment。同字符串的自增类型,不再阐述。\r\n 9)删除字段:DEL key field1 field2...删除指定KEY的一个或多个字段。\r\n 10)只获取字段名:HKEYS key。与HGETALL类似,但是只获取字段名,不获取字段值。\r\n 11)只获取字段值:HVALS key。与HGETALL类似,但是只获取字段值,不获取字段名。\r\n 12)获取字段数量:HLEN key。\r\n\r\n 5、Redis数据类型三列表类型:\r\n 列表类型存储了一个有序的字符串列表。常用的操作是向两端插入新的元素。时间复杂度为O(1)。结构为一个链表。记录头和尾的地址。看到这里,Redis数据类型的列表类型一个重大的作用呼之欲出,那就是队列。新来的请求插入到尾部,新处理过的从头部删除。另外,比如微博的新鲜事。比如日志。列表类型就是一个下标从0开始的数组。由于是链表存储,那么越靠近头和尾的元素操作越快,越靠近中间则越慢。\r\n\r\n 6、Redis数据命令三列表类型:\r\n 1)向头部插入:LPUSH key value1 value2...。返回增加后的列表长度。\r\n 2)向尾部插入:RPUSH key value1 value2...。返回增加后的列表长度。\r\n 3)从头部弹出:LPOP key。返回被弹出的元素值。该操作先删除key列表的第一个元素,再将它返回。\r\n 4)从尾部弹出:RPOP key。返回被弹出的元素值。\r\n 5)列表元素个数:LLEN key。key不存在返回0。\r\n 6)获取列表的子列表:LRANGE start end。返回第start个到第end个元素的列表。包含start和end。支持负数索引。-1表示最后一个元素,-2表示倒数第二个元素。\r\n 7)删除列表中指定值:LREM key count value。删除key这个列表中,所有值为value的元素,只删除count。如果有count+1个,那么就保留最后一个。count不存在或者为0,则删除所有的。如果count大于0,则删除从头到尾的count个,如果count小于0,则删除从尾到头的count个。\r\n 8)获取指定索引值:LINDEX key index。如LINDEX key 0就是列表的第一个元素。index可以是负数。\r\n 9)设置索引和值:LSET key index value。这个操作只是修改指定key且指定index的值。如果index不存在,则报错。\r\n 10)保留片段,删除其它:LTRIM key start end。保留start到end之间的所有元素,含start和end。其他全部删除。\r\n 11)向列表插入元素:LINSERT key BEFORE/AFTER value1 value2。从列表头开始遍历,发现值为value1时停止,将value2插入,根据BEFORE或者AFTER插入到value1的前面还是后面。\r\n 12)把一个列表的一个元素转到另一个列表:RPOPLPUSH list1 list2。将列表list1的右边元素删除,并把该与元素插入到列表list2的左边。原子操作。\r\n\r\n 7、Redis数据类型四集合类型:\r\n 集合类型是为了方便对多个集合进行操作和运算。集合中每个元素不同且没有顺序的概念,每个元素都是且只能是一个字符串。常用操作是对集合插入、删除、判断等操作。时间复杂度尾O(1)。可以进行交集、并集、差集运算。例如文章1的有3个标签,是一个Redis数据类型集合类型存储。文章2有3个标签,有一个Redis数据类型集合类型存储。文章是1是mysql,文章2是讲redis。那么交集是不是就交出了一个数据库?(假设数据库这个tag在两篇文字都有)。集合类型在redis中的存储是一个值为空的散列表。\r\n\r\n 8、Redis数据命令四集合类型:\r\n 1)增加:SADD key value。\r\n 2)删除:SREM key value。\r\n 3)获取指定集合的所有元素:SMEMBERS key。\r\n 4)判断某个元素是否存在:SISMEMBER key value。\r\n 5)差集运算:SDIFF key1 key2...。对多个集合进行差集运算。\r\n 6)交集运算:SINNER key1 key2...。对多个集合进行交集运算。\r\n 7)并集运算:SUNION key1 key2...。对多个集合进行并集运算。\r\n 8)获取集合中元素个数:SCARD key。返回集合中元素的总个数。\r\n 9)对差集、交集、并集运算的结果存放在一个指定的key中:SDIFFSTORE storekey key1 key2。对key1和key2求差集,结果存放在key为storekey的集合中。SINNERSTORE和SUNIONSTORE类似。\r\n 10)获取集合中的随即元素:SRANDMEMBER key [count]。参数count可选,如果count不存在,则随即一个。count大于0,则是不重复的count个元素。count小于0,则是一共|count|个元素,可以重复。\r\n 11)随即弹出一个元素:SPOP key。随即从集合中弹出一个元素并删除,将该元素的值返回。\r\n\r\n 9、Redis数据类型五有序集合类型:\r\n 集合类型是无序的,每个元素是唯一的。那么有序集合就是有序的,每个元素是唯一的。有序集合类型和集合类型的差别是,有序集合为每个元素配备了一个属性:分数。有序集合就是根据分数来排序的。有序集合是使用散列表和跳跃表实现的。所以和列表相比,操作中间元素的速度也很快。时间复杂度尾O(log(N))。Redis数据类型中的有序集合类型比Redis数据类型中的列表类型更加耗费资源。\r\n\r\n 10、Redis数据命令五有序集合类型:\r\n 1)增加:ZADD key sorce1 value1 sorce2 value2...。\r\n 2)获取分数:ZSCORE key value。获取key的有序集合中值为value的元素的分数。\r\n 3)获取排名在某个范围内的元素列表:ZRANFGE key start stop [WITHSCORE]。获取排名在start和end之间的元素列表,包含start和end2个元素。每个元素一行。如果有WITHSCORE参数,则一行元素值,一行分数。时间复杂度为O(LOGn+m)。如果分数相同,则0<0<A<Z<a<z。\r\n 4)获取指定分数范围的元素:ZRANGEBYSCORE key min max [WITHSCORE] [LIMIT offset count]。获取分数在min和max之间的元素列表。含两头。每个元素一行。如果有WITHSCORE参数,则一行元素值,一行分数。如果min大于max则顺序反转。\r\n 5)为某个元素增加分数:ZINCRBY key increment value。指定的有序集合的值为value的元素的分数+increment。返回值后更改后的分数。\r\n 6)获取集合中元素的数量:ZCARD key。\r\n 7)获取指定分数范围内的元素个数:ZCOUNT key min max。\r\n 8)删除一个或多个元素:ZREM key value1 value2...\r\n 9)根据排名范围删除元素:ZREMRANGEBYRANK key start end。删除排名在start和end中的元素。\r\n 10)按照分数范围删除元素:ZREMRANGEBYSCORE key min max。\r\n 11)获得元素排名(正序):ZRANK key value。获取value在该集合中的从小到大的排名。\r\n 12)获得元素排名(倒序):ZREVRANK key value。获取value在该集合中从大到小的排名。\r\n 13)有序集合的交集:ZINTERSTORE storekey key1 key2...[WEIGHTS weight [weight..]] [AGGREGATE SUM|MIN|MAX]。用来计算多个集合的交集,结果存储在storekey中。返回值是storekey的元素个数。AGGREGATE为SUM则storekey集合的每个元素的分数是参与计算的集合分数和。MIN是参与计算的分数最小值。MAX是参与计算分数最大值。WEIGHTS 设置每个集合的权重,如WEIGHTS 1 0.1。那么集合A的每个元素分数*1,集合B的每个元素分数*0.1\r\n 14)有序集合的并集:ZUNIONSTORE storekey key1 kye2...[WEIGHTS weight [weight..]] [AGGREGATE SUM|MIN|MAX]', 1399792603, 32, 23, 2),
(45, 10, '李轩Lane', 'Redis事务,Redis事务处理', 'Redis事务以及事务的处理方式,Redis的事务教程将在本篇讲解。事务功能在数据完整性和数据一致性发挥着不可或缺的巨大的作用。Redis数据库为我们提供了不那么完美的Redis事务功能和Redis事务操作。为什么提供了事务缺说不那么完美呢?', 'Redis事务_Redis事务教程_Redis事务处理', 'Redis事务的实现,Redis事务处理方式等Redis的事务教程将在本篇讲解。事务功能在数据完整性和数据一致性发挥着不可或缺的巨大的作用。Redis数据库为我们提供了不那么完美的Redis事务功能和Redis事务操作。', 'Redis事务,Redis事务教程,Redis事务处理', 'redis|事务|redis事务', 956, 'Redis事务以及事务的处理方式,Redis的事务教程将在本篇讲解。事务功能在数据完整性和数据一致性发挥着不可或缺的巨大的作用。Redis数据库为我们提供了不那么完美的Redis事务功能和Redis事务操作。为什么提供了事务缺说不那么完美呢?本篇是Redis事务教程之入门篇。\r\n 事务也是Redis的最小执行单位,是原子的,不怕高并发下的竞态条件。一个事务的多条命令语句要么都执行,要么都不执行。事务的应用也非常广泛。Redis事务的原理是先将一个事务命令发送给Redis,然后再让Redis一次执行这些命令。 如:\r\n[code]\r\nredis>MULTI\r\nOK\r\nredis>SADD user:10002:friendId 10001\r\nQUEUE\r\nredis>SADD user:10001:friendId 10002\r\nQUEUE\r\nredis>EXEC\r\n1)integer 1\r\n2)integer 1\r\n[/code]\r\n MULT告诉Redis开始一个事务,EXEC告诉Redis执行一个事务。中间两条SADD是属于同一个事务的语句。返回结果是第一条结果1,第二条语句结果1。如果,两条语句语法错误呢?\r\n[code]\r\nredis>MULTI\r\nOK\r\nredis>SADD user:10002:friendId 10001\r\nQUEUE\r\nredis>SADDDDDD user:10001:friendId 10002\r\nERR unknown command ‘SADDDDD’\r\nredis>EXEC\r\nERROR..............\r\n[/code]\r\n 如果语法错误,在录入Redis事务的过程中,Redis就会发现,并提示错误,在EXEC之后,仍然会返回错误提示,两条Redis事务的语句都不会被执行。可是,如果语法没有错误,可是在执行的时候却发生了错误呢?比如用SADD(集合类型的操作)去操作一个字符串数据类型的key呢?\r\n[code]\r\nredis>MULTI\r\nOK\r\nredis>SET num 1\r\nQUEUE\r\nredis>SADD num 2\r\nQUEUE\r\nredis>EXEC\r\n1)integer 1\r\n2)ERR ....................\r\n[/code]\r\n 可以看到,如果用集合类型的操作命令去操作字符串类型的key的话,第一条语句是正常执行并返回1.第二条语句却报错了。如果GET num可以的得到结果是1。也就是说即使有一条语句错误,但是第一条是执行成功了。这就是Redis提供了Redis事务机制,但是在操作中,不完美地方,Redis事务操作不提供所谓的回滚。\r\n 不过Redis却提供了一个新的办法,叫做WATCH命令。生病了,坐不住了。关于WATCH的内容下篇写。。', 1399897715, 15, 13, 0),
(46, 10, '李轩Lane', 'Redis WATCH命令详解,监听指定的键值变化', 'Redis WATCH是用来补充那不完美的事务功能,Redis WATCH命令监听指定KEY的变化。本篇是Redis WATCH详解部分。对Redis WATCH命令如何使用,有什么作用来进行讲解。', 'Redis WATCH_Redis WATCH命令_Redis WATCH详解', 'Redis WATCH是用来补充那不完美的事务功能,Redis WATCH命令监听指定KEY的变化。本篇是Redis WATCH详解部分。对Redis WATCH命令如何使用,有什么作用来进行讲解。', 'Redis WATCH,Redis WATCH命令,Redis WATCH详解', 'redis|redis watch', 1440, 'Redis WATCH是用来补充那不完美的事务功能,Redis WATCH命令监听指定KEY的变化。本篇是Redis WATCH详解部分。对Redis WATCH命令如何使用,有什么作用来进行讲解。WATCH是Redis事务系统中的一个成员命令。它有一个参数,是key。Redis WATCH命令的作用是监听在参数位置指定的key(key可以是五种类型中任意一种),如果这个key被修改或者被删除了,那么WATCH命令的监听作用就停止了,同时WATCH命令后面的第一个事务将不会被执行,直接跳过。\r\n[code]\r\nredis>SET num 1\r\nOK\r\nredis>WATCH num\r\nOK\r\nredis>SET num 2\r\nOK\r\nredis>MULTI\r\nOK\r\nredis>SET num 3\r\nQUEUE\r\nredis>EXEC\r\n(nil)\r\nredis>GET num\r\n"2"\r\n[/code]\r\n 上例可以看出,Redis的WATCH命令的作用。值的注意的时,在WATCH监听的key被修改或删除后,WATCH后的第一个事务不会被执行,但是第二个、第三个、第N个都是会正常执行的。', 1399987434, 12, 13, 0),
(47, 10, '李轩Lane', 'Redis生存时间,对Redis的键设置生存时间', 'Redis生存时间的概念,是和编程语言一样,拥有Redis生命周期,对键设定永久生效或指定生效时间。Redis过期删除键和值。Redis的生存时间和Redis的生命周期如何使用,本篇会详细讲解。', 'redis生存时间_redis生命周期_redis过期删除', 'Redis生存时间的概念,是和编程语言一样,拥有Redis生命周期,对键设定永久生效或指定生效时间。Redis过期删除键和值。Redis的生存时间和Redis的生命周期如何使用,本篇会详细讲解。', 'redis生存时间,redis生命周期,redis过期删除', 'redis|生存时间', 1858, 'Redis对键提供生存时间,在不指定生存时间时,生存时间是永久。时间到期后Redis会自动删除这个键。可以用EXPIRE命令,时间单位时秒,如果一个键是被设为有限的生存时间,那么在SET key进行重新赋值的时候会被再次设为永久:\r\n[code]\r\nSET session:captcha sd2a\r\nEXPIRE session:captcha 600\r\n[/code]\r\n 取消生存时间,将键的生存时间设为永久,是PERSIST:\r\n[code]\r\nPERSIST session:captcha\r\n[/code]\r\n 查看一个键的生存时间用TTL命令,-1表示永久或者以及到期被删除。\r\n[code]\r\nTTL session:captcha\r\n[/code]\r\n 在Redis的INCR,LPUSH,HSET,ZREM等命令时不会改变生存时间的。\r\n 想要精确到毫米来控制时间,就需要PEXPIRE即可,使用PTTL查看剩余时间。\r\n 如果想要给定一个到期的时间而不是多少秒后到期呢?就需要EXPIREAT和PEXPIREAT。EXPIREAT的参数是到期时的时间戳(秒),PEXPIREAT的参数是到期时间是时间戳(毫秒)\r\n[code]\r\nSET session:captcha sd2a\r\nEXPIREAT session:captcha 1399902009\r\nPEXPIREAT session:captcha 1399902009000\r\n[/code]\r\n\r\n\r\n 应用场景一:访问频率限制:我们限定每个用户1分钟只能浏览10个页面。伪代码如下:\r\n[code]\r\n$isExists = EXISTS limit:user1:192.168.1.2\r\nif($isExists){\r\n $num = INCR limit:user1:192.168.1.2\r\n if($num > 10){\r\n print ''超过限制''\r\n exit\r\n }\r\n}else{\r\n MULTI\r\n INCR limit:user1:192.168.1.2\r\n EXPIRE limit:user1:192.168.1.2 60\r\n EXEC\r\n}\r\n[/code]\r\n 我们用了事务的原因是因为,加入在执行了INCR limit:user1:192.168.1.2之后,在执行EXPIRE limit:user1:192.168.1.2 60之前,客户端被关闭了。那么这个键和值就会被持久化保存。且该ID终身只能访问10次了。这就太糟糕了。\r\n\r\n\r\n 应用场景二:实现缓存。计算一万名用户的排行榜,是很耗费资源的,那么我们把数据在第一次计算后存进一个key,然后对这个key设置生存时间。在1个小时后生存时间到期,key被删除,再次进行计算新排名并保存的一个临时key。我们用伪代码实现:\r\n[code]\r\n//战斗排行榜\r\n$rank = GET cache:rank:fight\r\nif not $rank\r\n $rank = 计算排名()\r\n MULTI\r\n SET cache:rank:fight $rank\r\n EXPIRE cache:rank:fight 3600\r\n EXEC\r\n[/code]\r\n Redis是内存存储的数据库,假如内存被缓存占满了,Redis会根据配置文件来删除一定的缓存。配置项是Redis的配置文件中的maxmemory参数,单位是字节。超过这个限制之后,会根据配置文件的maxmemory-policy参数来删除不需要的键。maxmemory-policy的可选规则是如下四种:\r\n 1、volatile-lru:使用LRU算法删除一个键(设置了生存时间的键)。\r\n 2、allkey-lru:使用LRU算法删除一个键。\r\n 3、volatile-random:随即删除一个键(设置了生存时间的键)。\r\n 4、allkey-random:随即删除一个键。\r\n 5、volatile-ttl:删除生存时间即将过期的一个键。是随即取出来N个键,然后删除N个键中即将过期的键,而不是遍历所有的键删除即将过期的。N是几?配置文件配的。\r\n 6、nevication:不删除,返回错误。', 1400070031, 13, 11, 0),
(48, 10, '李轩Lane', 'Redis SORT排序命令详解', 'Redis SORT提供排序功能,Redis SORT排序命令是最好用也是最复杂的redis命令之一。关系型数据库能完成的包括多表联合查询等能够都可以轻松完成。', 'Redis SORT_Redis SORT命令_Redis SORT排序', 'Redis SORT提供排序功能,Redis SORT排序命令是最好用也是最复杂的redis命令之一。关系型数据库能完成的包括多表联合查询等能够都可以轻松完成。', 'Redis SORT,Redis SORT命令,Redis SORT排序', 'redis|排序|redis SORT', 1220, 'Redis SORT是由Redis提供的一个排序命令。集合中的标签是无序的,可以使用SORT排序。如:\r\n[code]\r\nredis>SADD jihe 5\r\n(integer) 1\r\nredis>SADD jihe 1\r\n(integer) 1\r\nredis>SADD jihe 2\r\n(integer) 1\r\nredis>SADD jihe 8\r\n(integer) 1\r\nredis>SORT jihe\r\n1) "1"\r\n2) "2"\r\n3) "5"\r\n4) "8"\r\n[/code]\r\n 如果使用Redis SORT排序的不是数字,是字母,将他们按照字典的顺序排名,则需要使用\r\n[code]\r\nSORT jihe ALPHA\r\n[/code]\r\n 如果不加ALPHA参数,则会报错,提示:(error) ERR One or more scores can''t be converted into double。我们还可以使用关系型数据库的DESC进行倒序排序和LIMIT offset count来限定获取的条数\r\n[code]\r\nSORT jihe DESC LIMIT 0 2\r\n[/code]\r\n 还可以对Redis SORT命令添加BY参数。一条语句只能有一个BY参数。这时,SORT不会根据自身的值排序,比如(1,5,2,8和a,A,g,B),而是根据指定的另一个键中的字段来排序。如:\r\n[code]\r\nSORT tag:redis:article BY article:*->time DESC\r\n[/code]\r\n 解释:根据tag:redis:article中的值(tag是redis的文章ID),来组合成一个新的key就是article:(ag:redis:article中的一个值):time。获取到tag是redis的文章ID列表,然后根据他们的发布时间来排序。\r\n Redis SORT命令还有个GET参数,GET参数类似在关系型数据库中的关联查询。比如查询tag是redis的文章ID列表,将列表根据发布时间倒序排序,然后获取每个文章的标题。GET可以有多个:\r\n[code]\r\nSORT tag:redis:article BY article:*->time DESC GET article:*->title GET article:*->time GET #\r\n[/code]\r\n GET #的意思是,将文章ID返回回来,你可以写GET article:*->id,也可以写GET #。\r\n Redis SORT命令还有个参数是STORE,是将排序后的内容存储到一个新的key中。新key的类型是列表类型,如果存在则会覆盖。这个时候可以用EXPIRE来设置缓存:\r\n[code]\r\nSORT tag:redis:article BY article:*->time DESC GET article:*->title GET article:*->time GET # STORE resultKey\r\n[/code]\r\n Redis的SORT命令是Redis最复杂最强大的命令之一,时间复杂度是O(n+mLOGm)。n是待排序的列表长度,m是返回的元素个数。减少n和m会提高SORT的性能。', 1400160036, 10, 10, 0),
(49, 10, '李轩Lane', 'Redis队列如何实现?Redis栈逻辑如何实现', 'Redis队列如何实现?Redis栈原来是怎么样的?Redis的队列和栈机制是怎样的?本篇讲解Redis的队列技术。做为天生的队列好手,无论是先进先出的队列还是后进先出栈都是可以利用Redis来实现的。', 'Redis队列_redis栈_redis队列原理', 'Redis队列如何实现?Redis栈原来是怎么样的?Redis的队列和栈机制是怎样的?本篇讲解Redis的队列技术。做为天生的队列好手,无论是先进先出的队列还是后进先出栈都是可以利用Redis来实现的。', 'Redis队列,redis栈,redis队列原理', 'redis|队列|栈', 1119, 'Redis是天生的队列好手。RPOP,LPUSH就可以看到。生产者是队列任务的提出方,消费者队列任务的执行方。生产者提出大量的任务,他们排队一个接一个的被消费者执行。执行一个就RPOP,提出一个新任务就LPUSH。如果要插队呢?就RPUSH。Redis队列的伪代码:\r\n[code]\r\n//无限循环\r\nloop\r\n $task = RPOP queue\r\n if($task)\r\n execute($task)\r\n else\r\n sleep(1)\r\n[/code]\r\n 一个无限循环,从队列的最头部弹出一个任务,如果该任务存在则执行,如果不存在则睡眠,1秒后再次进入循环。这段代码实现了对队列任务的死循环来进行监听任务列表。这样并不好,每1秒扫描一次,如果一晚上都没有呢,那不是在白白浪费资源吗。这时候借助Redis 队列命令家族中的BRPOP。如果队列列表中有任务则弹出,如果没有任务就一直将连接阻塞,直到有新的任务加入才会放开。\r\n[code]\r\n//无限循环\r\nloop\r\n $task = BRPOP queue 0\r\n execute($task)\r\n[/code]\r\n BRPOP第一个参数是键,第二个参数是时间,如果时间为0则没有新任务加入的时候永久阻塞。\r\n Redis队列家族是可以进行优先级的。比如有三个列表任务列表,queue1,queue2,queue3。那么那个优先级高就拍在前面:\r\n[code]\r\n BRPOP queue2 queue3 queue1 0\r\n[/code]\r\n 如果队列2中有任务则优先弹出任务2。', 1400424825, 13, 14, 0),
(50, 10, '李轩Lane', 'Redis广播(订阅/发布者模式):在线实时聊天的基础', 'Redis的订阅发布者模式,是利用Redis构建在线实时聊天的理论年基础和实现原理基础。Redis订阅发布者模式可以实现广播功能,订阅一个频道,给一个频道的所有关注着发送广播内容。', 'Redis广播_Redis订阅发布_Redis在线聊天', 'Redis的订阅发布者模式,是利用Redis构建在线实时聊天的理论年基础和实现原理基础。Redis订阅发布者模式可以实现广播功能,订阅一个频道,给一个频道的所有关注着发送广播内容。', 'Redis广播,Redis订阅发布,Redis在线聊天', 'redis|聊天|广播', 1127, 'Redis是实时广播推送的一把好手。Redis提供了发布-订阅者模式。发布消息是PUBLISH 频道名 内容的格式。如:\r\n[code]\r\nPUBLISH fm97 hello world\r\n[/code]\r\n 这样,所有订阅fm7频道的用户就可以收到hello world了。PUBLISH返回值是收到的订阅者个数。订阅命令是SUBSCRIBE。如:\r\n[code]\r\nSUBSCRIBE fm97\r\n[/code]\r\n 在输入Redis订阅命令之后,值可以输入SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE/PUNSUBSCRIBE这四个命令,不然会报错。在SUBSCRIBE模式下,收到的消息第一行是subscribe。第二行是频道名称fm97,第三行是当前的订阅数量。也可能是收到的消息第一行是message。第二行是频道名称fm97,第三行是广播内容hello world。\r\n 退定则是UNSUBSCRIBE,如\r\n[code]\r\nUNSUBSCRIBE fm97\r\n[/code]\r\n PSUBSCRIBE 通过通配符来进行订阅,如:\r\n[code]\r\nPSUBSCRIBE fm?*\r\n[/code]\r\n 这就订阅了fm开头的所有频道,但不会订阅fm这个频道。\r\n PUNSUBSCRIBE同理。不说啦~', 1400509354, 14, 10, 0),
(51, 1, '李轩Lane', 'Memcached分布式部署方案设计(含PHP代码)', '一台Memcache通常不能满足我们的需求,这就需要分布式部署。Memcached分布式部署方案通常会采用两种方式,一种是普通Hash分布,一种是一致性Hash分布。本篇将以PHP作为客户端,来分析两种方案。', 'memcached分布式部署_memcached php_memcached集群', 'Memcached分布式部署方案通常会采用两种方式,一种是普通Hash分布,一种是一致性Hash分布。本篇将以PHP作为客户端,来分析两种方案。', 'memcached分布式部署,memcached php,memcached集群', 'PHP|Memcache|分布式', 4629, '一台Memcache通常不能满足我们的需求,这就需要分布式部署。Memcached分布式部署方案通常会采用两种方式,一种是普通Hash分布,一种是一致性Hash分布。本篇将以PHP作为客户端,来分析两种方案。\r\n 一、普通Hash分布:\r\n[code]\r\n<?php\r\nfunction test($key=''name''){\r\n $md5 = substr(md5($key), 0, 8);\r\n $seed = 31;\r\n $hash = 0;\r\n for($i=0; $i<8; $i++){\r\n $hash = $hash * $seed + ord($md5[$i]);\r\n }\r\n return $hash & 0x7FFFFFFF;\r\n}\r\n\r\n$memcacheList = array(\r\n array(''host''=>''192.168.1.2'', ''port''=>6379),\r\n array(''host''=>''192.168.1.3'', ''port''=>6379),\r\n array(''host''=>''192.168.1.4'', ''port''=>6379),\r\n array(''host''=>''192.168.1.5'', ''port''=>6379),\r\n);\r\n$key = ''username'';\r\n$value = ''lane'';\r\n//根据KEY获取hash\r\n$hash = $this->test($key);\r\n$count = count($memcacheList);\r\n$memcache = $memcacheList[$hash % $count];\r\n$mc = new Memcached($memcache);\r\n$mc->set($key, $value);\r\n?>\r\n[/code]\r\n 代码很简单,一个Hash函数,根据所需要的key,将他md5后取前8位,然后经过Hash算法返回一个整数。将这个整数对服务器总数求模。得到的就是服务器列表的编号。这种方式的缺点是服务器数量改变后,同一个key不同hash,将取不到值了。\r\n\r\n 二、一致性Hash分布\r\n 一致性Hash尽管也会造成数据的丢失,但是损失是最小的。\r\n 将2的32次方-1想象成一个圆环,服务器列表在上面排列。根据key通过hash算法求得在圆环上的位置,那么所需要的服务器的位置在key的位置前面最近的一个(顺时针)。\r\n[code]\r\n<?php\r\nclass FlexiHash{\r\n //服务器列表\r\n private $serverList = array();\r\n //是否排序\r\n private $isSort = false;\r\n\r\n /**\r\n * Description: Hash函数,将传入的key以整数形式返回\r\n * @param string $key\r\n * @return int\r\n */\r\n private function myHash($key){\r\n $md5 = substr(md5($key), 0, 8);\r\n $seed = 31;\r\n $hash = 0;\r\n for($i=0; $i<8; $i++){\r\n $hash = $hash * $seed + ord($md5[$i]);\r\n }\r\n return $hash & 0x7FFFFFFF;\r\n }\r\n\r\n /**\r\n * Description: 添加新服务器\r\n * @param $server\r\n */\r\n public function addServer($server){\r\n $hash = $this->myHash($server);\r\n if(!isset($this->serverList[$hash])){\r\n $this->serverList[$hash] = $server;\r\n }\r\n $this->isSort = false;\r\n return true;\r\n }\r\n\r\n /**\r\n * Description: 删除指定服务器\r\n * @param $server\r\n * @return bool\r\n */\r\n public function removeServer($server){\r\n $hash = $this->myHash($server);\r\n if(isset($this->serverList[$hash])){\r\n unset($this->serverList[$hash]);\r\n }\r\n $this->isSort = false;\r\n return true;\r\n }\r\n\r\n /**\r\n * Description: 根据要操作的KEY返回一个操作的服务器信息\r\n * @param $key\r\n * @return mixed\r\n */\r\n public function lookup($key){\r\n //将指定的KEYhash出一个整数\r\n $hash = $this->myHash($key);\r\n if($this->isSort !== true){\r\n krsort($this->serverList);\r\n $this->isSort = false;\r\n }\r\n foreach($this->serverList as $key=>$server){\r\n if($key <= $hash){\r\n return $server;\r\n }\r\n }\r\n return array_pop($this->serverList);\r\n }\r\n}\r\n//使用方法\r\n$mc = new FlexiHash();\r\n$mc->addServer(''192.168.1.2'');\r\n$mc->addServer(''192.168.1.3'');\r\n$mc->addServer(''192.168.1.4'');\r\n$mc->addServer(''192.168.1.5'');\r\n\r\necho ''KEY=key1时,操作的服务器为:''.$mc->lookup(''key1'').''<br>'';\r\necho ''KEY=key1时,操作的服务器为:''.$mc->lookup(''key2'').''<br>'';\r\necho ''KEY=key1时,操作的服务器为:''.$mc->lookup(''key3'').''<br>'';\r\necho ''KEY=key1时,操作的服务器为:''.$mc->lookup(''key4'').''<br>'';\r\necho ''KEY=key1时,操作的服务器为:''.$mc->lookup(''key5'').''<br>'';\r\n?>\r\n[/code]', 1400549934, 21, 16, 1),
(52, 11, '李轩Lane', '高性能网站架构方案', '高性能网站架构方案,本文谈了七点网站架构方案,用以优化网站响应时间,实现大型网站技术架构方案。无论是电子商务或者其他网站且可使用。', '网站架构_大型网站技术架构_高性能网站架构方案', '高性能网站架构方案,本文谈了七点网站架构方案,用以优化网站响应时间,实现大型网站技术架构方案。无论是电子商务或者其他网站且可使用。', '网站架构,大型网站技术架构,高性能网站架构方案', '架构', 2983, '高性能网站架构方案,本文谈了七点网站架构方案,用以优化网站响应时间,实现大型网站技术架构方案。无论是电子商务或者其他网站且可使用。\r\n一、优化网站响应时间的架构方案:\r\n 网站能不能留的住用户,一方面是看内容,另一方面是看响应时间。通常有以下几个方式来降低网站响应时间:\r\n 1、减少HTTP请求。包括合并css和javascript。减少图片数量,比如利用css的偏移技术来在一个图片中选择不同的位置内容。利用浏览器的Cache功能,我们可以在头中声明是否被浏览器缓存。\r\n 2、动态内容静态化。比如永久生成HTML文件。生成静态文件并设定生存时间,到期后查询新的动态内容进行替换。\r\n 3、优化数据库。数据库的性能对于项目整体性能中是重中之重。设计良好的Mysql比乱糟糟的Mysql性能高出N个数量级,更别论再引入NOSQL了,比如Redis,MongoDB。\r\n 4、使用负载均衡。将请求合理的分发到更多服务器。\r\n 5、使用缓存。把花费时间和资源成本高昂的计算结果取出缓存起来,避免重复计算。比如在Mysql前面挡一层Memcached。比如生成一个文件,使用的时候include进来。再比如PHP中的OPCACHE等。\r\n\r\n二、压力测试的架构方案:\r\n 吞吐率是指单位时间内处理的请求数,单位reqs/s。最大吞吐率是指单位时间内能够处理的最大请求出。模拟足够多的人数和并发请求来测试最大吞吐率的方法叫做压力测试。比如Apache自带的ab(Apache Bench)。ab的参数很多,常用的有请求数(-n),并发用户数(-c),超时时间(-t),长连接(-k),附件一个Cookie(-c name=value)\r\n[code]\r\n$ab -c 10 -n 1000 http://localhost/\r\n[/code]\r\n\r\n三、长连接的架构方案:\r\n 每次请求都需要TCP的三次握手,握手完比表示连接正式联通,之后再发送数据。那么,把N个请求,就需要3N次握手,传递N次数据,得到N次响应,总共5N。如果把N个请求合成一个请求,就是3次握手,1次传递数据,1次返回响应,共5次。但是,有时候我们需要上一次响应的返回结果来发送新一轮的请求,在这个时候,合并请求并不好实现,这就需要长连接。使用起来很简单,在头中包含如下:\r\n[code]\r\nConnection: Keep-Alive\r\n[/code]\r\n 客户端和服务器端都可以设置长连接的最大时间,当两者不统一时以小的一方为准。开启长连接后进行压力测试:\r\n[code]\r\n$ab -c 10 -n 1000 http://localhost/\r\n[/code]\r\n 发现提升不止三五倍。本机是提升了8倍的性能。\r\n\r\n四、提高Mysql的响应速度的架构方案:\r\n Handlerocker是日本的一位架构师开发。Mysql的一种插件。Handlerocker实现了绕过Mysql的SQL解析层。在Mysql5.1以上版本可以使用,详情可以查看Mysql手册。这里就不在阐述。\r\n\r\n五、Mysql主从复制的架构方案:\r\n 在分布式部署中,1台主库,N台从库。主库只写,从库只查。主库从库数据需要实现统一,这就是主从复制。优点是:\r\n 1、从库备份时,主库可以继续处理更新。\r\n 2、优化响应时间。\r\n 3、增加健壮性。主库挂了可以切换到从库作为备份。\r\n 主从复制的实现过程有三步,1个在主库,2个在从库:\r\n 1、主库服务器将用户对数据库更新的操作以二进制格式保存到Binary Log日志文件。然后Binlog Dump线程将Binary Log日志文件传输给从库服务器。\r\n 2、从库服务器通过一个I/O线程将主库服务器的Binary Log日志文件中的更新操作复制到一个叫做Relay Log中的中继日志文件中。\r\n 3、从库服务器通过另一个SQL线程Relay Log中继日志文件中的操作依次在本地执行,从而实现主从数据库之间数据的同步。\r\n 本篇只是简单的列出方案,详细的配置和实现步骤将在另一篇中写到。\r\n\r\n六、代理的架构方案:\r\n 读取内存的速度是读取硬盘的100000-1000000倍。把访问过的页面缓存在内存中,下次直接从内存中读取,可以有效加速。\r\n 1、传统代理。客户端发送请求给代理服务器,代理服务器向WEB服务器取到数据并返回给浏览器。代理服务器就是一个有大的存储空间的Cache。\r\n 2、反向代理。和传统代理原理类似,只是使用对象不同。传统代理的使用对象是客户端,反向代理的使用对象是服务器。用户通过反向代理访问Web服务器,Web服务器是隐藏起来的。不过用户不关心这些,权把代理服务器当作真实的Web服务器。反向代理有Vamish。\r\n\r\n七、异步计算的架构方案:\r\n 比较耗时的比如将用户上传的文件分发到多台机器,比如裁剪图片,视频转码等。可以使用异步方案。让用户无须等待计算结束而是先行返回结果。代表产品有和Memcache同一家的Gearman。关于Gearman的使用可以查看PHP手册。', 1400658261, 35, 16, 1);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(53, 1, '李轩Lane', 'Hash表:使用PHP实现Hash表功能', 'Hash表作为最重要的数据结构之一,也叫做散列表。使用PHP实现Hash表的功能。PHP可以模拟实现Hash表的增删改查。通过对key的映射到数组中的一个位置来访问。映射函数叫做Hash函数,存放记录的数组称为Hash表。', 'Hash表_PHP实现Hash表_Hash函数', 'Hash表作为最重要的数据结构之一,也叫做散列表。使用PHP实现Hash表的功能。PHP可以模拟实现Hash表的增删改查。通过对key的映射到数组中的一个位置来访问。映射函数叫做Hash函数,存放记录的数组称为Hash表。', 'Hash表,PHP实现Hash表,Hash函数', 'PHP|Hash', 2061, 'Hash表作为最重要的数据结构之一,也叫做散列表。使用PHP实现Hash表的功能。PHP可以模拟实现Hash表的增删改查。通过对key的映射到数组中的一个位置来访问。映射函数叫做Hash函数,存放记录的数组称为Hash表。\r\nHash函数把任意长度的和类型的key转换成固定长度输出。不同的key可能拥有相同的hash。\r\nHash表的时间复杂度为O(1)\r\n[code]\r\n<?php\r\n/**\r\n * hash表类\r\n * Class HashTable\r\n * Auth Lane\r\n * Mail [email protected]\r\n * Blog http://www.lanecn.com\r\n */\r\nclass HashTable{\r\n private $arr = array();\r\n private $size = 10;\r\n public function __construct(){\r\n //SplFixedArray创建的数组比一般的Array()效率更高,因为更接近C的数组。创建时需要指定尺寸\r\n $this->arr = new SplFixedArray($this->size);\r\n }\r\n\r\n /**\r\n * Description: 简单hash算法。输入key,输出hash后的整数\r\n * @param $key\r\n * @return int\r\n */\r\n private function simpleHash($key){\r\n $len = strlen($key);\r\n //key中每个字符所对应的ASCII的值\r\n $asciiTotal = 0;\r\n for($i=0; $i<$len; $i++){\r\n $asciiTotal += ord($key[$i]);\r\n }\r\n return $asciiTotal % $this->size;\r\n }\r\n\r\n /**\r\n * Description: 赋值\r\n * @param $key\r\n * @param $value\r\n * @return bool\r\n */\r\n public function set($key, $value){\r\n $hash = $this->simpleHash($key);\r\n $this->arr[$hash] = $value;\r\n return true;\r\n }\r\n\r\n /**\r\n * Description: 取值\r\n * @param $key\r\n * @return mixed\r\n */\r\n public function get($key){\r\n $hash = $this->simpleHash($key);\r\n return $this->arr[$hash];\r\n }\r\n\r\n public function getList(){\r\n return $this->arr;\r\n }\r\n\r\n public function editSize($size){\r\n $this->size = $size;\r\n $this->arr->setSize($size);\r\n }\r\n}\r\n?>\r\n[/code]\r\n下面对我们的HashTable进行测试。\r\n[code]\r\n<?php\r\n//测试1\r\n$arr = new HashTable();\r\nfor($i=0; $i<15; $i++){\r\n $arr->set(''key''.$i, ''value''.$i);\r\n}\r\nprint_r($arr->getList());\r\n//SplFixedArray Object\r\n//(\r\n// [0] => value14\r\n// [1] => value4\r\n// [2] => value5\r\n// [3] => value6\r\n// [4] => value7\r\n// [5] => value8\r\n// [6] => value10\r\n// [7] => value11\r\n// [8] => value12\r\n// [9] => value13\r\n//)\r\n//不同的key可能产生相同的hash值,那么赋值的时候后操作会覆盖前操作。\r\n\r\n//测试2\r\n$arr->editSize(15);\r\nfor($i=0; $i<15; $i++){\r\n $arr->set(''key''.$i, ''value''.$i);\r\n}\r\nprint_r($arr->getList());\r\n//SplFixedArray Object\r\n//(\r\n// [0] => value14\r\n// [1] => value4\r\n// [2] => value0\r\n// [3] => value1\r\n// [4] => value2\r\n// [5] => value3\r\n// [6] => value10\r\n// [7] => value11\r\n// [8] => value12\r\n// [9] => value13\r\n// [10] => value14\r\n// [11] => value9\r\n// [12] =>\r\n// [13] =>\r\n// [14] =>\r\n//)\r\n?>\r\n[/code]\r\n 改变了值之后可以存放更多的元素。但是仍然存在不同的key可能产生相同的hash值,那么赋值的时候后操作会覆盖前操作的问题。这种冲突的问题我们来用拉链法解决。\r\n\r\n 拉链法解决冲突。拉链法解决冲突的做法是将所有的相同Hash值的key放在一个链表中,比如key3和key14在hash之后都是0,那么在数组的键为0的地方存储这两个值,形式是链表。如果不能理解我的文字,请看下面的示例,看一下打印信息就明白了。拉链法是什么,就是链表。\r\n 创建一个HashNode类,用来存储key和value的值,并且存储相同hash的另一个元素。在同一条链上,查找越后的元素越费时。时间复杂度为O(n).\r\n[code]\r\n<?php\r\nclass HashNode{\r\n public $key;\r\n public $value;\r\n public $nextNode;\r\n public function __construct($key, $value, $nextNode=Null){\r\n $this->key = $key;\r\n $this->value = $value;\r\n $this->nextNode = $nextNode;\r\n }\r\n}\r\nclass NewHashTable{\r\n private $arr;\r\n private $size = 10;\r\n public function __construct(){\r\n $this->arr = new SplFixedArray($this->size);\r\n }\r\n private function simpleHash($key){\r\n $asciiTotal = 0;\r\n $len = strlen($key);\r\n for($i=0; $i<$len; $i++){\r\n $asciiTotal += ord($key[$i]);\r\n }\r\n return $asciiTotal % $this->size;\r\n }\r\n public function set($key, $value){\r\n $hash = $this->simpleHash($key);\r\n if(isset($this->arr[$hash])){\r\n $newNode = new HashNode($key, $value, $this->arr[$hash]);\r\n }else{\r\n $newNode = new HashNode($key, $value, null);\r\n }\r\n $this->arr[$hash] = $newNode;\r\n return true;\r\n }\r\n public function get($key){\r\n $hash = $this->simpleHash($key);\r\n $current = $this->arr[$hash];\r\n while(!empty($current)){\r\n if($current->key == $key){\r\n return $current->value;\r\n }\r\n $current = $current->nextNode;\r\n }\r\n return NULL;\r\n }\r\n public function getList(){\r\n return $this->arr;\r\n }\r\n}\r\n?>\r\n[/code]\r\n 对我们新的HashTable进行测试\r\n[code]\r\n<?php\r\n//测试1\r\n$newArr = new NewHashTable();\r\nfor($i=0; $i<30; $i++){\r\n $newArr->set(''key''.$i, ''value''.$i);\r\n}\r\nprint_r($newArr->getList());\r\nvar_dump($newArr->get(''key3''));\r\n//SplFixedArray Object\r\n//(\r\n// [0] => HashNode Object\r\n//(\r\n// [key] => key23\r\n// [value] => value23\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key14\r\n// [value] => value14\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key3\r\n// [value] => value3\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [1] => HashNode Object\r\n//(\r\n// [key] => key24\r\n// [value] => value24\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key15\r\n// [value] => value15\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key4\r\n// [value] => value4\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [2] => HashNode Object\r\n//(\r\n// [key] => key25\r\n// [value] => value25\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key16\r\n// [value] => value16\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key5\r\n// [value] => value5\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [3] => HashNode Object\r\n//(\r\n// [key] => key26\r\n// [value] => value26\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key17\r\n// [value] => value17\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key6\r\n// [value] => value6\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [4] => HashNode Object\r\n//(\r\n// [key] => key27\r\n// [value] => value27\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key18\r\n// [value] => value18\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key7\r\n// [value] => value7\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [5] => HashNode Object\r\n//(\r\n// [key] => key28\r\n// [value] => value28\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key19\r\n// [value] => value19\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key8\r\n// [value] => value8\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [6] => HashNode Object\r\n//(\r\n// [key] => key29\r\n// [value] => value29\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key10\r\n// [value] => value10\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key9\r\n// [value] => value9\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [7] => HashNode Object\r\n//(\r\n// [key] => key20\r\n// [value] => value20\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key11\r\n// [value] => value11\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key0\r\n// [value] => value0\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [8] => HashNode Object\r\n//(\r\n// [key] => key21\r\n// [value] => value21\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key12\r\n// [value] => value12\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key1\r\n// [value] => value1\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n// [9] => HashNode Object\r\n//(\r\n// [key] => key22\r\n// [value] => value22\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key13\r\n// [value] => value13\r\n// [nextNode] => HashNode Object\r\n//(\r\n// [key] => key2\r\n// [value] => value2\r\n// [nextNode] =>\r\n// )\r\n//\r\n// )\r\n//\r\n// )\r\n//\r\n//)\r\n//string(6) "value3"\r\n?>\r\n[/code]', 1400747962, 8, 9, 1),
(54, 1, '李轩Lane', '用PHP的实现一个高效的数据库(文件存储,NOSQL)', '本文用PHP开发高性能非关系型数据库,利用文件存储+hash表实现。提供php实现数据库的代码。本文将详细讲解用php开发数据库的案例。', 'PHP开发高性能非关系型数据库_php实现数据库_php开发数据库', '本文用PHP开发高性能非关系型数据库,利用文件存储+hash表实现。提供php实现数据库的代码。本文将详细讲解用php开发数据库的案例。', 'PHP开发高性能非关系型数据库,php实现数据库,php开发数据库', 'PHP|Hash|数据库', 2967, '用文件的方式读写,一个文件是索引文件,另一个文件是真实的数据文件。\r\n索引文件分为2部分,第一部分是所有的指针,记录第二部分的位置;第二部分是索引记录。所有的索引指针:是记录所有相同Hash值的key的指针,它是一个链表结构,记录在数据文件的位置和同key的下一个值。\r\n索引记录中:每条记录有四部分,第一部分4个字节,是下一条索引的偏移量;第二部分是该记录的key,128字节;第三部分是数据偏移量,4个字节;第四部分是数据记录长度,4个字节。\r\n我们设定文件的存储上限为262144个。\r\n\r\n查找流程如下:\r\n1、根据key算出hash值,获取该hash值的链表在索引文件的第一部分(所有指针区)的位置。\r\n2、根据步骤一的位置,获取值,时间复杂度O(1);\r\n2、根据步骤一中的值,找到索引文件中第二部分(索引记录)的位置,也就是和key相同hash值的所有指针的链表。顺着链表查找该key,获取该key在链表中存放的数据,数据只包含该key在索引文件中的位置,时间复杂度为O(n);\r\n3、根据步骤二所获取的key在索引文件位置,得到索引文件中存放该key的信息。信息包含在真实数据文件中存放真实数据的位置。\r\n4、根据步骤三所获取的位置,在真实数据文件中获取数据,并返回给应用程序。\r\n\r\n测试结果:插入10000条耗时:793ms。查找10000条耗时:149ms。虽然这效率只有Redis的十分之一。。。但是请不要在意这些细节。。。\r\n\r\n代码做了注释,上述文字有些乱。代码只实现三个方法,一个插入(如果存在则跳过),一个是查找,一个是删除。\r\n\r\n思路来源:《PHP核心技术与最佳实践》一书。尊重作者,转载请保留该书名。\r\n[code]\r\n<?php\r\n//Hash表中的元素指针个数,每个指针都是int,存储hash链表的文件偏移量\r\ndefine(''DB_BUCKET_SIZE'', 262144);\r\n//每条记录的key的长度\r\ndefine(''DB_KEY_SIZE'', 128);\r\n//一条索引记录的长度\r\ndefine(''DB_INDEX_SIZE'', DB_KEY_SIZE + 12);\r\n\r\n//成功-返回码\r\ndefine(''DB_SUCCESS'', 1);\r\n//失败-返回码\r\ndefine(''DB_FAILURE'', -1);\r\n//key重复-返回码\r\ndefine(''DB_KEY_EXISTS'', -2);\r\n\r\nclass DB{\r\n private $idx_fp;\r\n private $dat_fp;\r\n private $closed;\r\n\r\n /**\r\n * Description: 打开数据库\r\n * @param $pathName 数据文件的存放路径\r\n * @return mixed\r\n */\r\n public function open($pathName){\r\n $idx_path = $pathName . ''.idx'';\r\n $dat_path = $pathName . ''.dat'';\r\n if(!file_exists($idx_path)){\r\n $init = true;\r\n $mode = "w+b";\r\n }else{\r\n $init = false;\r\n $mode = ''r+b'';\r\n }\r\n $this->idx_fp = fopen($idx_path, $mode);\r\n if(!$this->idx_fp){\r\n return DB_FAILURE;\r\n }\r\n if($init){\r\n //把0x00000000转换成无符号长整型的二进制\r\n $elem = pack(''L'', 0x00000000);\r\n for($i=0; $i< DB_BUCKET_SIZE; $i++){\r\n fwrite($this->idx_fp, $elem, 4);\r\n }\r\n }\r\n $this->dat_fp = fopen($dat_path, $mode);\r\n if(!$this->dat_fp){\r\n return DB_FAILURE;\r\n }\r\n\r\n return DB_SUCCESS;\r\n }\r\n\r\n /**\r\n * Description: Times33 Hash算法\r\n * @param $key\r\n * @return int\r\n */\r\n private function times33Hash($key){\r\n $len = 8;\r\n $key = substr(md5($key), 0, $len);\r\n $hash = 0;\r\n for($i=0; $i<$len; $i++){\r\n $hash += 33 * $hash + ord($key[$i]);\r\n }\r\n //0x7FFFFFFF:一个十六进制的数是4bit,8个就是32位,就是4字节,和一个int一样大。而F是1111,7是0111,那么这个十六进制的数就是头为0,其余为1的,首位是符号位,也就是说7fffffff是最大的整数。\r\n //& 0x7fffffff 可以保证返回的数是正整数\r\n return $hash & 0x7FFFFFFF;\r\n }\r\n\r\n /**\r\n * Description: 插入记录\r\n * @param $key\r\n * @param $value\r\n */\r\n public function add($key, $value){\r\n $offset = ($this->times33Hash($key) % DB_BUCKET_SIZE) * 4;\r\n\r\n $idxoff = fstat($this->idx_fp);\r\n $idxoff = intval($idxoff[''size'']);\r\n\r\n $datoff = fstat($this->dat_fp);\r\n $datoff = intval($datoff[''size'']);\r\n\r\n $keylen = strlen($key);\r\n $vallen = strlen($value);\r\n if($keylen > DB_KEY_SIZE){\r\n return DB_FAILURE;\r\n }\r\n //0表示这是最后一个记录,该链再无其他记录。\r\n $block = pack(''L'', 0x00000000);\r\n //键值\r\n $block .= $key;\r\n //如果键值的长度没有达到最大长度,则用0填充\r\n $space = DB_KEY_SIZE - $keylen;\r\n for($i=0; $i<$space; $i++){\r\n $block .= pack(''C'', 0x00);\r\n }\r\n //数据所在文件的偏移量\r\n $block .= pack(''L'', $datoff);\r\n //数据记录的长度\r\n $block .= pack(''L'', $vallen);\r\n //尽管SEEK_SET是默认值,但是显式声明了就不怕以后官方会改变了-.-\r\n fseek($this->idx_fp, $offset, SEEK_SET);\r\n //检测该key所对应的hash值是否存在了\r\n $pos = @unpack(''L'', fread($this->idx_fp, 4));\r\n $pos = $pos[1];\r\n //如果key不存在\r\n if($pos == 0){\r\n fseek($this->idx_fp, $offset, SEEK_SET);\r\n fwrite($this->idx_fp, pack(''L'', $idxoff), 4);\r\n\r\n fseek($this->idx_fp, 0, SEEK_END);\r\n fwrite($this->idx_fp, $block, DB_INDEX_SIZE);\r\n\r\n fseek($this->dat_fp, 0, SEEK_END);\r\n fwrite($this->dat_fp, $value, $vallen);\r\n\r\n return DB_SUCCESS;\r\n }\r\n //如果key存在\r\n $found = false;\r\n while($pos){\r\n fseek($this->idx_fp, $pos, SEEK_SET);\r\n $tmp_block = fread($this->idx_fp, DB_INDEX_SIZE);\r\n $cpkey = substr($tmp_block, 4, DB_KEY_SIZE);\r\n //$cpkey==$key时返回0,小于返回负数,大于返回正数\r\n if(!strncmp($cpkey, $key, $keylen)){\r\n $dataoff = unpack(''L'', substr($tmp_block, DB_KEY_SIZE + 4, 4));\r\n $dataoff = $dataoff[1];\r\n $datalen = unpack(''L'', substr($tmp_block, DB_KEY_SIZE + 8, 4));\r\n $datalen = $datalen[1];\r\n $found = true;\r\n break;\r\n }\r\n $prev = $pos;\r\n $pos = @unpack(''L'', substr($tmp_block, 0, 4));\r\n $pos = $pos[1];\r\n }\r\n\r\n if($found){\r\n return DB_KEY_EXISTS;\r\n }\r\n fseek($this->idx_fp, $prev, SEEK_SET);\r\n fwrite($this->idx_fp, pack(''L'', $idxoff), 4);\r\n fseek($this->idx_fp, 0, SEEK_END);\r\n fwrite($this->idx_fp, $block, DB_INDEX_SIZE);\r\n fseek($this->dat_fp, 0, SEEK_END);\r\n fwrite($this->dat_fp, $value, $vallen);\r\n return DB_SUCCESS;\r\n }\r\n\r\n /**\r\n * Description: 查询一条记录\r\n * @param $key\r\n */\r\n public function get($key){\r\n //计算偏移量,key的hash值对索引文件的大小求模,再乘4。因为每个链表指针大小为4\r\n $offset = ($this->times33Hash($key) % DB_BUCKET_SIZE) * 4;\r\n //SEEK_SET是默认的\r\n fseek($this->idx_fp, $offset, SEEK_SET);\r\n $pos = unpack(''L'', fread($this->idx_fp, 4));\r\n $pos = $pos[1];\r\n\r\n $found = false;\r\n while($pos){\r\n fseek($this->idx_fp, $pos, SEEK_SET);\r\n $block = fread($this->idx_fp, DB_INDEX_SIZE);\r\n $cpkey = substr($block, 4, DB_KEY_SIZE);\r\n\r\n if(!strncmp($key, $cpkey, strlen($key))){\r\n $dataoff = unpack(''L'', substr($block, DB_KEY_SIZE + 4, 4));\r\n $dataoff = $dataoff[1];\r\n\r\n $datalen = unpack(''L'', substr($block, DB_KEY_SIZE + 8, 4));\r\n $datalen = $datalen[1];\r\n\r\n $found = true;\r\n break;\r\n }\r\n $pos = unpack(''L'', substr($block, 0, 4));\r\n $pos = $pos[1];\r\n }\r\n if(!$found){\r\n return null;\r\n }\r\n fseek($this->dat_fp, $dataoff, SEEK_SET);\r\n $data = fread($this->dat_fp, $datalen);\r\n return $data;\r\n }\r\n\r\n /**\r\n * Description: 删除\r\n * @param $key\r\n */\r\n public function delete($key){\r\n $offset = ($this->times33Hash($key) % DB_BUCKET_SIZE) * 4;\r\n fseek($this->idx_fp, $offset, SEEK_SET);\r\n $head = unpack(''L'', fread($this->idx_fp, 4));\r\n $head = $head[1];\r\n $curr = $head;\r\n $prev = 0;\r\n $found = false;\r\n while($curr){\r\n fseek($this->idx_fp, $curr, SEEK_SET);\r\n $block = fread($this->idx_fp, DB_INDEX_SIZE);\r\n\r\n $next = unpack(''L'', substr($block, 0, 4));\r\n $next = $next[1];\r\n\r\n $cpkey = substr($block, 4, DB_KEY_SIZE);\r\n if(!strncmp($key, $cpkey, strlen($key))){\r\n $found = true;\r\n break;\r\n }\r\n $prev = $curr;\r\n $curr = $next;\r\n }\r\n if(!$found){\r\n return DB_FAILURE;\r\n }\r\n //删除索引文件。\r\n if($prev == 0){\r\n fseek($this->idx_fp, $offset, SEEK_SET);\r\n fwrite($this->idx_fp, pack(''L'', $next), 4);\r\n }else{\r\n fseek($this->idx_fp, $prev, SEEK_SET);\r\n fwrite($this->idx_fp, pack(''L'', $next), 4);\r\n }\r\n return DB_SUCCESS;\r\n }\r\n\r\n public function close(){\r\n if(!$this->closed){\r\n fclose($this->idx_fp);\r\n fclose($this->dat_fp);\r\n $this->closed = true;\r\n }\r\n }\r\n}\r\n?>\r\n[/code]\r\n\r\n测试,测试添加一万条和查找一万条:\r\n[code]\r\n<?php\r\n//先include上面的类。。如果在同一个文件中就不用了。\r\n//测试\r\n$db = new DB();\r\n$db->open(''/var/www/data/'');\r\n\r\n$startTime = microtime(true);\r\n\r\n//插入测试...插入10000条:成功,耗时: 793.48206520081ms\r\n//for($i=0; $i<10000; $i++){\r\n// $db->add(''key''.$i, ''value''.$i);\r\n//}\r\n\r\n//查找测试...查找10000条:成功,耗时: 149.08313751221ms\r\nfor($i=0; $i<10000; $i++){\r\n $db->get(''key''.$i);\r\n}\r\n\r\n$endTime = microtime(true);\r\necho ''成功,耗时: '' . (($endTime - $startTime)*1000) . ''ms'';\r\n$db->close();\r\n?>\r\n[/code]', 1400839724, 20, 14, 1),
(55, 10, '李轩Lane', 'Redis管道概念', 'Redis管道的概念类似与Liinux管道的概念,可以将多个命令合成一个命令,减少了多次的TCP三次握手。提高Redis在应用层的效率,节省传输时间。', 'Redis管道_Redis管道概念', 'Redis管道的概念类似与Liinux管道的概念,可以将多个命令合成一个命令,减少了多次的TCP三次握手。提高Redis在应用层的效率,节省传输时间。', 'Redis管道,Redis管道概念', 'Redis|管道', 919, 'Redis管道是大幅提升传输速度和用户体验的一个功能。Redis是使用TCP协议进行传输,当客户端发送一条命令之后,到服务器端接收到这个命令,这个过程是需要发送时间的。当服务器处理完命令返回结果给客户端的时候,这个是需要返回时间的。统称为往返时延。如果多条命令一次性发送过去,Redis服务器端全部处理好后一次性发送回客户端,那么就只需要花费一份的往返时延。这就是Redis的管道命令。Redis底层通信协议对管道命令提供了支持。如:\r\n[code]\r\n$ (echo -en "PING\\r\\nPING\\r\\nPING\\r\\n"; sleep 1) | nc localhost 6379\r\n+PONG\r\n+PONG\r\n+PONG\r\n[/code]\r\n 关于管道的具体使用,后期在详细说明。这里只需要知道有这个概念即可。', 1400992957, 16, 13, 0),
(56, 10, '李轩Lane', 'Redis在PHP中的应用', 'PHP redis的使用方法详解。php上使用redis主要有两种方式,一种是Predis,一种是phpredis。phpredis是php的一个扩展,以C语言编写的高性能链表。本文讲解Predis的使用。Predis是PHP语言编写。', 'php redis_php redis扩展_php redis使用', 'PHP redis的使用方法详解。php上使用redis主要有两种方式,一种是Predis,一种是phpredis。phpredis是php的一个扩展,以C语言编写的高性能链表。本文讲解Predis的使用。Predis是PHP语言编写。', 'php redis,php redis扩展,php redis使用', 'PHP|Redis', 1599, 'PHP redis的使用方法详解。php上使用redis主要有两种方式,一种是Predis,一种是phpredis。phpredis是php的一个扩展,以C语言编写的高性能链表。本文讲解Predis的使用。Predis是PHP语言编写。\r\n\r\n PHP redis的使用方法详解。php上使用redis主要有两种方式,一种是Predis,一种是phpredis。phpredis是php的一个扩展,以C语言编写的高性能链表。本文讲解Predis的使用。\r\n Predis是Redis官方推出的由PHP原生语言编写的客户端。由于Predis采用了命名空间的方式,所以Predis要求PHP版本最低为5.3。 \r\n Predis开源且托管在GitHub上https://github.com/nrk/predis/。下载整个文件夹复制到项目目录即可。\r\n[code]\r\n//引入autoload.php文件\r\nrequire ''./predis/autoload.php'';\r\n\r\n//实例化\r\n$redis = New Predis\\Client();\r\n/*这个是简化版,等同于$redis = New Predis\\Client(array(\r\n * ''scheme'' => ''tcp'',\r\n * ''host'' => ''127.0.0.1''\r\n * ''port'' => 6379\r\n *));\r\n */\r\n\r\n//GET\r\n$redis->get(''key'');\r\n\r\n//LPUSH\r\n$redis->lpush(''key'', ''1'', ''2'', ''3'');\r\n\r\n//MSET 相当于$redis->MSET(''article:1:title'', ''biaoti'', ''article:1:content'', ''neirong'', ''ctime'', ''shijian'');\r\n$article = array(''article:1:title''=>''biaoti'', ''article:1:content''=>''neirong'', ''article:1:ctime''=>''shijian'');\r\n$redis->MSET(''key'', $article);\r\n\r\n//MGET\r\n$articleKeys = array_keys($article);\r\n$redis->MGET($articleKeys);\r\n\r\n//SORT\r\n//SORT articleList BY article:*->time LIMIT 0 10 GET article:*->title GET # DESC ALPHA STORE storeKey\r\n$sort = array(\r\n ''by'' => ''article:*->time'',\r\n ''limit'' => array(0, 10),\r\n ''get'' => array(''article:*->title'', ''#''),\r\n ''sort'' => ''desc'',\r\n ''alpha'' => true,\r\n ''store'' => ''storeKey''\r\n);\r\n[/code]\r\n Predis的封装之后,用起来非常方便,关联数组的引入是开发效率非常高的。更多的内容可以参考Predis文档:https://github.com/nrk/predis/blob/v0.8/FAQ.md', 1401113824, 15, 22, 0),
(57, 1, '李轩Lane', 'PHP的命名空间', 'PHP命名空间是PHP5.3开始支持。本篇讲解PHP命名空间用法和PHP命名空间详解。它的诞生使的我们在一个文件中可以使用多个同名的类而不冲突。', 'PHP命名空间_PHP命名空间用法_PHP命名空间详解', 'PHP命名空间是PHP5.3开始支持。本篇讲解PHP命名空间用法和PHP命名空间详解。它的诞生使的我们在一个文件中可以使用多个同名的类而不冲突。', 'PHP命名空间,PHP命名空间用法,PHP命名空间详解', 'PHP|命名空间', 1052, 'PHP命名空间是PHP5.3开始支持。本篇讲解PHP命名空间用法和PHP命名空间详解。它的诞生使的我们在一个文件中可以使用多个同名的类而不冲突。\r\n 好处:我们的项目有一个记录日志文件的类,叫做Log。然后我们又必须要引入另一个代码包,这个代码包里也有一个叫做Log的类。那么在一个文件中,我们记录日志的又需要给两个类都写一条日志。可以类同名了,怎么办?这个时候,命名空间应运而生。在Java等语言中命名空间是很早就已经提供了支持,而我大PHP一直到5.3才对命名空间提供了支持。\r\n 示例一:\r\n文件index.php\r\n[code]\r\n<?php\r\ninclude ''test.php'';\r\n\r\nclass index{\r\n public function a(){\r\n echo basename(__FILE__);\r\n echo ''<br>'';\r\n echo __CLASS__ . '' : '' . __METHOD__;\r\n }\r\n}\r\n$obj = new index();\r\n$obj->a();\r\necho ''<br>'';\r\n$obj1 = new test\\index();\r\n$obj1->a();\r\n?>\r\n[/code]\r\n文件test.php\r\n[code]\r\n<?php\r\nnamespace test;\r\nclass index{\r\n public function a(){\r\n echo basename(__FILE__);\r\n echo ''<br>'';\r\n echo __CLASS__ . '' : '' . __METHOD__;\r\n }\r\n}\r\n?>\r\n[/code]\r\n 我们给index.php不设置命名空间,对test.php设置命名空间,名为test。运行index.php。\r\n结果:\r\n[code]\r\nindex.php\r\nindex : index::a\r\ntest.php\r\ntest\\index : test\\index::a\r\n[/code]\r\n 我们看到了,同名的类也可以运行而不冲突了。\r\n\r\n 示例二:\r\n文件index.php\r\n[code]\r\n<?php\r\nnamespace index;\r\ninclude ''test.php'';\r\n\r\nclass index{\r\n public function a(){\r\n echo basename(__FILE__);\r\n echo ''<br>'';\r\n echo __CLASS__ . '' : '' . __METHOD__;\r\n }\r\n}\r\n$obj = new index();\r\n$obj->a();\r\necho ''<br>'';\r\n$obj1 = new \\test\\index();\r\n$obj1->a();\r\n?>\r\n[/code]\r\n文件test.php\r\n[code]\r\n<?php\r\nnamespace test;\r\nclass index{\r\n public function a(){\r\n echo basename(__FILE__);\r\n echo ''<br>'';\r\n echo __CLASS__ . '' : '' . __METHOD__;\r\n }\r\n}\r\n?>\r\n[/code]\r\n 我们给index.php设置命名空间,名为index,对test.php设置命名空间,名为test。运行index.php。\r\n结果:\r\n[code]\r\nindex.php\r\nindex\\index : index\\index::a\r\ntest.php\r\ntest\\index : test\\index::a\r\n[/code]\r\n 比较示例一和二,不对index.php设置命名空间,即该文件是整个PHP全局命名空间下面的一个文件,那么使用test\\index()即可,如果对index.php设置命名空间,即在其他的命名空间中使用命名空间,就要多一个“\\”,就要使用\\test\\index()。\r\n\r\n示例三:\r\n文件index.php\r\n[code]\r\n<?php\r\nnamespace index;\r\n\r\ninclude ''namespace.php'';\r\n\r\nuse \\test\\test1\\test2 as test2;\r\n\r\nclass index{\r\n public function a(){\r\n echo basename(__FILE__);\r\n echo ''<br>'';\r\n echo __CLASS__ . '' : '' . __METHOD__;\r\n }\r\n}\r\n\r\n$obj = new index();\r\n$obj->a();\r\n\r\necho ''<br>'';\r\n\r\n$obj1 = new \\test\\test1\\test2\\index();\r\n$obj1->a();\r\n\r\necho ''<br>'';\r\n\r\n$obj1 = new test2\\index();\r\n$obj1->a();\r\n[/code]\r\n文件test.php\r\n[code]\r\n<?php\r\nnamespace test\\test1\\test2;\r\nclass index{\r\n public function a(){\r\n echo basename(__FILE__);\r\n echo ''<br>'';\r\n echo __CLASS__ . '' : '' . __METHOD__;\r\n }\r\n}\r\n[/code]\r\n结果:\r\n[code]\r\nindex.php\r\nindex\\index : index\\index::a\r\ntest.php\r\ntest\\test1\\test2\\index : test\\test1\\test2\\index::a\r\ntest.php\r\ntest\\test1\\test2\\index : test\\test1\\test2\\index::a\r\n[/code]\r\n 这说明了什么?别名!用过SQL吧。\r\n[code]\r\nselect COUNT(*) as `count` from `tebleName`\r\n[/code]\r\n 嗯,一个意思。\\test\\test1\\test2这个名字太长了,就别名为test2就好了。使用了use之后呢,这个命名空间就想到于是在index这个命名空间下面了,而不是全局命名空间的一员了,所以使用test2\\index(),而不是\\test2\\index()。\r\n\r\n 别名时在PHP代码编译的时候执行的,而变量的解析则要更晚。也就是说不能对变量运用use关键字。示例如下(摘自官方手册示例):\r\n[code]\r\n<?php\r\nuse My\\Full\\Classname as Another, My\\Full\\NSname;\r\n\r\n$obj = new Another; // 实例化一个 My\\Full\\Classname 对象\r\n$a = ''Another'';\r\n$obj = new $a; // 实际化一个 Another 对象\r\n[/code]', 1402203105, 12, 11, 0),
(58, 1, '李轩Lane', 'XHProf的安装和使用(PHP性能测试神器)', 'XHProf是Facebook开发的性能调试工具,帮助我们的PHP程序性能调优,更加健壮。XHProf安装和使用方法将在本章讲解。XHProf是PHP的PECL扩展。没有XDeBug那些耗费资源,更加的小巧。', 'XHProf安装_XHProf使用_XHProf详解', 'XHProf是Facebook开发的性能调试工具,帮助我们的PHP程序性能调优,更加健壮。XHProf安装和使用方法将在本章讲解。XHProf是PHP的PECL扩展。', 'XHProf安装,XHProf使用,XHProf详解', 'PHP|XHProf', 4347, 'XHProf是Facebook开发的性能调试工具,帮助我们的PHP程序性能调优,更加健壮。XHProf安装和使用方法将在本章讲解。XHProf是PHP的PECL扩展。没有XDeBug那些耗费资源,更加的小巧。\r\n 流程:程序开头打点,结尾打点。那么XHProf机会记录在两个点之间的所有代码响应时所耗费的时间、内存、CPU等各项指标,我们也可以知道一次请求调用了多少次MySQL,多少次Memcache,更加直观的指明优化道路。\r\n 安装:\r\n[code]\r\n------------下载并编译PHP-XHProf源码------------\r\nwget http://pecl.php.net/get/xhprof-0.9.4.tgz\r\ntar -zxvf xhprof-0.9.4.tgz\r\ncd xhprof-0.9.4\r\ncd extension\r\nphpize\r\n./configure --enable-xhprof\r\nmake\r\nmake test\r\nsudo make install\r\n\r\n------------修改php.ini---------------\r\nsudo vim /etc/php.ini\r\n#在php.ini最下方加入以下:\r\nextension=xhprof.so\r\nxhprof.output_dir="/var/www/xhprof"\r\n\r\n-----------重启Apache--------------\r\nsudo apache restart\r\n[/code]\r\n\r\n 进入刚才解压的安装包文件夹中,将xhprof_lib和xhprof_html复制到项目目录下。\r\n 接下来,建立一个头文件head.php,这是要打两个点中的开头的点:\r\n[code]\r\n//head.php\r\n<?php\r\nif(extension_loaded(''xhprof'')){\r\n //载入下载的XHPROF包中的2个文件夹\r\n include_once ''xhprof_lib/utils/xhprof_lib.php'';\r\n include_once ''xhprof_lib/utils/xhprof_runs.php'';\r\n xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);\r\n}\r\n[/code]\r\n\r\n 再建立一个底部文件foot.php,这是要打两个点中的结尾的点:\r\n[code]\r\n//foot.php\r\n<?php\r\nif(extension_loaded(''xhprof'')){\r\n $ns = ''myXhprof'';\r\n //关闭profiler\r\n $xhprofData = xhprof_disable();\r\n //实例化类\r\n $xhprofRuns = new XHProfRuns_Default();\r\n $runId = $xhprofRuns->save_run($xhprofData, $ns);\r\n //前端展示库的URL\r\n $url = ''http://localhost/xhprof_html/index.php'';\r\n $url .= ''?run=%s&source=%s'';\r\n //变量替换\r\n $url = sprintf($url, $runId, $ns);\r\n //输入URL\r\n echo ''<a href="''.$url.''" target="_blank">查看结果</a>'';\r\n}\r\n[/code]\r\n\r\n 使用的最后一步:打点。现在我们建立一个测试文件index.php。测试我大Hello World。\r\n[code]\r\n//index.php\r\n<?php\r\ninclude_once ''head.php'';\r\necho ''Hello World'';\r\ninclude_once ''foot.php'';\r\n[/code]\r\n \r\n 可以看到,在http://localhost/index.php中,最下面是我们在foot.php中写的“查看结果”,点击进去,可以看到本次请求所使用到的所有函数的列表,每个函数所耗费的时间、CPU、Memory等信息,点击第一栏可以根据所选排序。点击[View Full Callgraph]可以看到由本列表所生成的流程图,从入口到哪个函数,又到哪个函数,这个函数调用了哪个函数,这个函数调用了多少次Memcache等,一幕了然。减少MC的调用,减少这个,减少那个,请求的响应速度能不快吗?\r\n\r\n技巧:\r\n 我有1000个文件,现在我需要用XHProf检测一下我整个项目,难道要每个文件头部和尾部都要加上include吗?\r\n 在php.ini中添加:\r\n[code]\r\nauto_prepend_file = /var/www/head.php\r\nauto_append_file = /var/www/foot.php\r\n[/code]\r\n 或者在.htaccess中添加\r\n[code]\r\nphp_value auto_prepend_file = /var/www/head.php\r\nphp_value auto_append_file = /var/www/foot.php\r\n[/code]\r\n\r\n报错:\r\n1、点击[View Full Callgraph]查看图片的时候报错:failed to execute cmd:" dot -Tpng". stderr:`sh: dot:command not found`。\r\n原因:原因:未安装图形化工具\r\n解决:\r\n[code]\r\n//红帽系列\r\nyum install graphviz\r\n//Ununtu\r\napt-get install graphviz\r\n//OS X\r\nbrew install graphviz\r\n[/code]', 1402647138, 2438, 11, 2),
(59, 6, '李轩Lane', '百度PHP面试,流程与面试问题', '百度PHP面试会问什么?很荣幸参加了百度PHP岗位面试,互联网自由与分享精神,不涉及公司机密的百度PHP面试题公布。望能帮助到将百度PHP面试的同学', '百度PHP面试_百度PHP面试题_百度PHP岗位面试', '百度PHP面试会问什么?很荣幸参加了百度PHP岗位面试,互联网自由与分享精神,不涉及公司机密的百度PHP面试题公布。望能帮助到将百度PHP面试的同学', '百度PHP面试,百度PHP面试题,百度PHP岗位面试', 'PHP|面试|百度', 4284, '很荣幸参加了百度PHP岗位面试,百度PHP面试会问什么是大家最关心的,鉴于互联网自由与分享精神,不涉及公司机密的百度PHP面试题公布。望能帮助到将去百度PHP面试同学。\r\n Ps:1、部门省略。2、涉及公司情况的省略。时隔一个月,具体的已经记不大清楚了,仅供参考。\r\n\r\n一面:技术面\r\n 1、介绍自己\r\n 2、介绍项目\r\n 3、分布式部署方式\r\n 4、项目优化经验\r\n 5、XHProf\r\n 6、大文件,里面都是数字,一行一个,排序\r\n 6、以上5点的扩展\r\n\r\n二面:技术面\r\n 1、介绍项目\r\n 2、XHProf\r\n 3、介绍项目优化经验\r\n 4、写几个PHP自带的函数\r\n 5、一个关联数组(KEY-VALUE键值对,KEY是字符串,VALUE是整数),在不使用PHP自带函数的前提下,对数组排序,然后问时间复杂度。最后问写完后问还能不能优化,怎么优化。\r\n 6、不断的边聊边扩展。\r\n\r\n三面:技术面\r\n 1、画一下项目的流程图\r\n 2、介绍一下项目\r\n 3、项目优化经验\r\n 4、为什么离职\r\n 5、你想做什么\r\n 6、边聊便扩展\r\n\r\n等通知...\r\n\r\n邮件告知面试已过...\r\n\r\nHR电话谈薪资和offer...', 1403882929, 9, 4, 2),
(62, 1, '李轩Lane', '四则运算:中缀表达式转后缀表达式', '四则运算表达式,我们书面使用的叫做中缀表达式,而计算器,却更加喜欢后缀表达,括号优先级,加减乘除优先级等使得运算中缀四则表达式变得困难。这个时候引入了一种计算机喜欢的格式,叫做后缀表达式。本文以PHP代码,实现中缀表达式转后缀表达式的逻辑。', '中缀表达式_后缀表达式_中缀表达式转后缀表达式', '四则运算表达式,我们书面使用的叫做中缀表达式,而计算器,却更加喜欢后缀表达,括号优先级,加减乘除优先级等使得运算中缀四则表达式变得困难。这个时候引入了一种计算机喜欢的格式,叫做后缀表达式。本文以PHP代码,实现中缀表达式转后缀表达式的逻辑。', '中缀表达式,后缀表达式,中缀表达式转后缀表达式', 'PHP|后缀|中缀', 1473, '<p> 四则运算表达式,我们书面使用的叫做中缀表达式,而计算器,却更加喜欢后缀表达,括号优先级,加减乘除优先级等使得运算中缀四则表达式变得困难。这个时候引入了一种计算机喜欢的格式,叫做后缀表达式。本文以PHP代码,实现中缀表达式转后缀表达式的逻辑。</p><p> 本文以PHP为代码环境,有人会说高级语言直接写表达式就好了,它们会算,可是他们为什么会算,怎么算的,还是需要把中缀表达式转为后缀表达式。因此本文代码只是模拟一个逻辑。</p><p> 比如:传统的四则运算表达式(中缀表达式)是9 + ( 3 - 1 ) * 3 + 10 / 2,对应的后缀表达式就是9 3 1 - 3 * + 10 2 / +。</p><p> 转换逻辑:一个字符一个字符的输入,如果是数字则直接输出;如果是左括号则直接入栈;如果是右括号则开始出栈,直到遇到第一次左括号为止;如果是加减乘除,则判断,如果栈顶也是符号,且输入的符号的优先级不高于栈顶的符号优先级,则全部出栈,否则该输入的符号入栈。</p><p>[code]</p><p><?php</p><p>/**</p><p> * 将输入的字符按照中缀表达式转后缀表达式的规则处理</p><p> * @param $str 输入的字符</p><p> * @param $stack 栈</p><p> * @param $newStrList 新的表达式</p><p> */</p><p>function suffix($str, &$stack, &$newStrList){</p><p> //如果是数字则输出</p><p> if(is_numeric($str)){</p><p> $newStrList .= $str . ' ';</p><p> }</p><p> //如果是左括号则入栈</p><p> else if($str == '('){</p><p> $stack[] = $str;</p><p> }</p><p> //如果是右括号则将最近的左括号之前的所有数据出栈</p><p> else if($str == ')'){</p><p> while($arrPop = array_pop($stack)){</p><p> if($arrPop == '('){</p><p> break;</p><p> }</p><p> $newStrList .= $arrPop . ' ';</p><p> }</p><p> }</p><p> //如果是加减乘除则判断与栈顶符号优先级</p><p> else if(in_array($str, array('+', '-', '*', '/')) && count($stack) > 0){</p><p> $key = (count($stack) - 1);</p><p> if(in_array($stack[$key], array('+', '-', '*', '/'))){</p><p> //该符号优先级不高于栈顶符号的</p><p> if(checkPriority($str, $stack[$key]) != 1){</p><p> for($i=$key; $i>=0; $i--){</p><p> if($stack[$i] == '('){</p><p> break;</p><p> }</p><p> $newStrList .= $stack[$i] . ' ';</p><p> unset($stack[$i]);</p><p> $stack = array_values($stack);</p><p> }</p><p> }</p><p> }</p><p> //本次的符号入栈</p><p> $stack[] = $str;</p><p> }else{</p><p> $stack[] = $str;</p><p> }</p><p>}</p><p><br/></p><p>/**</p><p> * 判断运算符的优先级</p><p> * @param $operatorA</p><p> * @param $operatorB</p><p> * @return A大于B返回1,A等于B返回0,A小于B返回-1</p><p> */</p><p>function checkPriority($operatorA, $operatorB){</p><p> switch($operatorA){</p><p> case '+':</p><p> case '-':</p><p> if($operatorB == '+' || $operatorB == '-'){</p><p> return 0;</p><p> }else if($operatorB == '*' || $operatorB == '/'){</p><p> return -1;</p><p> }</p><p> break;</p><p> case '*':</p><p> case '/':</p><p> if($operatorB == '+' || $operatorB == '-'){</p><p> return 1;</p><p> }else if($operatorB == '*' || $operatorB == '/'){</p><p> return 0;</p><p> }</p><p> break;</p><p> default:</p><p> exit('error');</p><p> }</p><p>}</p><p>//栈</p><p>$stack = array();</p><p>//待转换的表达式</p><p>$strList = '9 + ( 3 - 1 ) * 3 + 10 / 2';</p><p>//新的表达式</p><p>$newStrList = '';</p><p>$strList = explode(' ', $strList);</p><p>foreach($strList as $str){</p><p> if($str != ' '){</p><p> suffix($str, $stack, $newStrList);</p><p> }</p><p>}</p><p>//数组反转</p><p>while($s = array_pop($stack)){</p><p> $newStrList .= $s . ' ';</p><p>}</p><p>echo $newStrList;</p><p>[/code]</p><p><br/></p>', 1404441511, 0, 0, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(63, 1, '李轩Lane', '二叉树遍历算法', '二叉树遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。二叉树遍历算法,主要有三种二叉树遍历算法,分别是前序、中序、后序算法。不常用的还有层序遍历算法。', '二叉树遍历_二叉树遍历算法_二叉树三种遍历算法', '二叉树遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。二叉树遍历算法,主要有三种二叉树遍历算法,分别是前序、中序、后序算法。不常用的还有层序遍历算法。', '二叉树遍历,二叉树遍历算法,二叉树三种遍历算法', 'PHP|二叉树|算法', 1865, '<p> 二叉树遍历,是值从根节点出发,按照某种次序依次访问二叉树中的所有节点,使得每个节点被访问一次且仅被访问依次。</p><p style="text-align: center;"><img src="http://lanecn-upload.stor.sinaapp.com/image/20140709_1404896527_874807.gif" title="20140709_1404896527_874807.gif" alt="tupan062.gif" style="text-align: center; white-space: normal;"/></p><p style="text-align: center;">图是百度搜的。。。谢谢提供图的英雄。。<br/></p><p> 前序遍历二叉树:如果二叉树为空则返回,若二叉树非空,则先遍历左树,再遍历右树,遍历顺序为ABCDEGF。</p><p> 中序遍历二叉树:如果二叉树为空则返回,若二叉树非空,则从根节点开始,中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树,遍历顺序为CBEGDFA。</p><p> 后序遍历二叉树:如果二叉树为空则返回,若二叉树非空,则从左到右先叶子后节点的访问遍历访问左右子树,最后是访问根节点。访问顺序为CGEFDBA。</p><p> 层序遍历二叉树:如果二叉树为空则返回,若二叉树非空,则从树的第一层,也就是根节点开始访问,从上而下逐层遍历,在同一层中,按照从左到右的顺序对节点逐个访问。访问顺序为ABCDEFG。</p><p><br/></p><p> 现在,我们用PHP代码,来遍历二叉树结构。二叉树是放一个大数组,每一个节点都有三个字段,data表示这个节点的值,lChild表示这个节点的左边子节点,rChild表示这个节点的右边子节点。二叉树的结构我们用上面那张图。</p><p><br/></p><p>二叉树结构代码如下:</p><p>[code]</p><p><?php</p><p>//二叉树</p><p>$arr = array(</p><p> 'data' => 'A',</p><p> 'lChild' => array(</p><p> 'data' => 'B',</p><p> 'lChild' => array(</p><p> 'data' => 'C',</p><p> 'lChild' => array(),</p><p> 'rChild' => array(),</p><p> ),</p><p> 'rChild' => array(</p><p> 'data' => 'D',</p><p> 'lChild' => array(</p><p> 'data' => 'E',</p><p> 'lChild' => array(),</p><p> 'rChild' => array(</p><p> 'data' => 'G',</p><p> 'lChild' => array(),</p><p> 'rChild' => array(),</p><p> ),</p><p> ),</p><p> 'rChild' => array(</p><p> 'data' => 'F',</p><p> 'lChild' => array(),</p><p> 'rChild' => array(),</p><p> ),</p><p> ),</p><p> ),</p><p> 'rChild' => array(),</p><p>);</p><p>[/code]</p><p><br/></p><p>遍历算法一:前序遍历二叉树<br/></p><p>[code]</p><p><?php</p><p>//前序遍历二叉树算法</p><p>echo '前序遍历二叉树算法:';</p><p>PreOrderTraverse($arr);</p><p>echo '<Br>';</p><p>function PreOrderTraverse($node){</p><p> if(empty($node)){</p><p> return;</p><p> }</p><p> //输出值</p><p> print_r($node['data']);</p><p> //左节点</p><p> PreOrderTraverse($node['lChild']);</p><p> //右节点</p><p> PreOrderTraverse($node['rChild']);</p><p>}</p><p>[/code]</p><p><br/></p><p>遍历算法二:中序遍历二叉树</p><p>[code]</p><p><?php</p><p>//中序遍历二叉树算法</p><p>echo '中序遍历二叉树算法:';</p><p>inOrderTraverse($arr);</p><p>echo '<Br>';</p><p>function inOrderTraverse($node){</p><p> if(empty($node)){</p><p> return;</p><p> }</p><p> //左节点</p><p> inOrderTraverse($node['lChild']);</p><p> //输出值</p><p> print_r($node['data']);</p><p> //右节点</p><p> inOrderTraverse($node['rChild']);</p><p>}</p><p>[/code]</p><p><br/></p><p style="white-space: normal;">遍历算法三:后序遍历二叉树</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;"><?php</p><p>//后序遍历二叉树算法</p><p>echo '后序遍历二叉树算法:';</p><p>postOrderTraverse($arr);</p><p>echo '<Br>';</p><p>function postOrderTraverse($node){</p><p> if(empty($node)){</p><p> return;</p><p> }</p><p> //左节点</p><p> postOrderTraverse($node['lChild']);</p><p> //右节点</p><p> postOrderTraverse($node['rChild']);</p><p> //输出值</p><p> print_r($node['data']);</p><p>}</p><p style="white-space: normal;">[/code]</p>', 1404890917, 1, 0, 0),
(64, 6, '李轩Lane', '2014书目,自勉!', '2014年看过的书,一方面自己给自己鼓劲,一方面给大家推荐推荐。其实就是记录一下2014年看了那些书,而已。', '2014年看过的书,一方面自己给自己鼓劲,一方面给大家推荐推荐', '2014年看过的书,一方面自己给自己鼓劲,一方面给大家推荐推荐。其实就是记录一下2014年看了那些书,而已。', '2014年看过的书,一方面自己给自己鼓劲,一方面给大家推荐推荐', '书', 2812, '<p>这篇其实就是记录一下2014年我所看书的list。</p><p>看到记录的增加,脑袋的知识增加,满满的成就感,很开心。</p><p><br/></p><p>3月13:《高性能Mysql》 30%</p><p>4月15-5月4日:《大话设计模式》完结</p><p>4月:《Mysql必知必会》完结</p><p>5月:《Redis入门指南》完结</p><p>5月:《Python简明教程》完结</p><p>5月:《PHP核心技术与最佳实践》 完结。这本书推荐一下</p><p>6月:《MongoDB权威指南》 20%</p><p>6月:《PHP精粹》完结</p><p>6月:《莽荒记》 更新中……</p><p>6月25日 - 7月18日:《大话数据结构》完结</p><p>7月13日:《C程序语言设计》 50%</p><p>7月17日-8月底:《高性能程序员的修炼》完结</p><p>8月11日:《C语言入门经典》完结</p><p>8月12日-8月21日:《天才在左,疯子在右》完结</p><p>8月:《大主宰》更新中……</p><p>9月:《完美世界》更新中……</p><p>9月20:《PHP之道》完结</p><p>9月24:《PHP最佳实践》完结</p><p>9月25日:《深入理解PHP内核 - Thinking In PHP Internals》1%</p><p>10月24日:《啊哈!算法》50%<br/></p><p><br/></p><p>很多时候都是同时看几本,公司看一本,家里看一本。路上看小说。。。</p><p><br/></p>', 1405577446, 6, 3, 0),
(65, 1, '李轩Lane', '微信PHP快速开发框架LaneWeChat', '微信框架LaneWeChat,是微信PHP开发框架,经过中国联通、游戏公司等多家公司使用的微信PHP框架,轻量、高效。完美的封装了PHP微信开发的功能点,只需要调用封装好的函数,5分钟上手,10分钟精通PHP微信开发。LaneWeChat微信开发框架为全国大中小企业提供了接入微信的好机会。', '微信框架_微信PHP框架_微信开发框架_LaneWeChat', '微信框架LaneWeChat,是微信PHP开发框架,经过中国联通、游戏公司等多家公司使用的微信PHP框架,轻量、高效。完美的封装了PHP微信开发的功能点,只需要调用封装好的函数,5分钟上手,10分钟精通PHP微信开发。LaneWeChat微信开发框架为全国大中小企业提供了接入微信的好机会。', '微信框架,微信PHP框架,微信开发框架,LaneWeChat', 'LaneWeChat|PHP|微信|框架', 29500, '<p style="text-align: center;"><br/></p><p style="text-align: center;"><strong><span style="color: rgb(255, 0, 0); font-size: 18px;">框架内容已经正式迁移到了</span></strong><a href="http://lanewechat.lanecn.com" _src="http://lanewechat.lanecn.com" style="color: rgb(255, 0, 0); text-decoration: underline; font-size: 18px;"><strong><span style="color: rgb(255, 0, 0); font-size: 18px;">http://lanewechat.lanecn.com</span></strong></a><strong><span style="color: rgb(255, 0, 0); font-size: 18px;">请移步,点击</span></strong><a href="http://lanewechat.lanecn.com" target="_self" style="color: rgb(255, 0, 0); text-decoration: underline; font-size: 18px;"><strong><span style="color: rgb(255, 0, 0); font-size: 18px;">PHP微信开发框架</span></strong></a><strong><span style="color: rgb(255, 0, 0); font-size: 18px;">传送</span></strong></p><p><span style="color: rgb(255, 0, 0);"><br/></span></p><p><br/></p><p><br/></p><p>框架名称:LaneWeChat 微信开发框架PHP</p><p><br/></p><p>框架版本:1.2</p><p><br/></p><p>框架简介:这是一个为快速开发微信应用而生的PHP框架。将微信的开发者功能根据文档进行了封装。为了快速开发的目的,开发者完全不需要要知道具体是如何实现的,只需要简单的调用方法即可。微信框架LaneWeChat,经过中国联通、奇虎360等多家公司使用的微信PHP框架,轻量、高效。完美的封装了PHP微信开发的功能点,只需要调用封装好的函数,5分钟上手,10分钟精通PHP微信开发。LaneWeChat微信开发框架为全国大中小企业提供了接入微信的好机会。</p><p><br/></p><p>开发语言:PHP</p><p><br/></p><p>版本要求:原则PHP5.3以上</p><p><br/></p><p>版本规避:若版本低于PHP5.3,则删除本框架所有页面开头namespace一行即可。</p><p><br/></p><p>命名空间:本框架的命名空间均为LaneWeChat开头。</p><p><br/></p><p>下载地址:https://github.com/lixuancn/LaneWeChat/archive/master.zip</p><p><br/></p><p>GitHub:https://github.com/lixuancn/LaneWeChat</p><p><br/></p><p>目前还没有微信支付这些,只有微信公众账号的相关功能,希望大家闲暇时间也可以贡献代码。</p><p><br/></p><p><br/></p><p>Developer Blog:<a href="http://www.lanecn.com" _src="http://www.lanecn.com">http://www.lanecn.com</a> </p><p><br/></p><p>文档地址:<a href="http://www.lanecn.com/article/main/aid-65" _src="http://www.lanecn.com/article/main/aid-65">http://www.lanecn.com/article/main/aid-65</a> </p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>更新日志:</p><p><br/></p><p> 2014-08-17:1.2版本。新增自定义菜单功能,多媒体上传与下载(media_id的获取途径)。更新说明:<a href="http://www.lanecn.com/article/main/aid-66" _src="http://www.lanecn.com/article/main/aid-66">http://www.lanecn.com/article/main/aid-66</a></p><p><br/></p><p> 2014-08-07:1.0版本</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>文档目录:</p><p><br/></p><p> 1、常识普及。</p><p><br/></p><p> 2、如何安装。</p><p><br/></p><p> 3、初出茅庐。</p><p><br/></p><p> 4、流程分析。</p><p><br/></p><p> 5、牛刀小试。</p><p><br/></p><p> 6、函数详解。</p><p><br/></p><p> 7、实例示范。</p><p><br/></p><p><br/></p><p>常识普及:</p><p><br/></p><p>一、微信公众账号分两种,一种是订阅号,一种是服务号。</p><p><br/></p><p> 1、订阅号是被动响应用户消息功能,并且每天推送一条消息。</p><p><br/></p><p> 2、服务号是300元/每年认证,被动响应用户消息,主动给用户发送消息,自定义菜单按钮,网页授权等功能,并且每月推送一条消息。</p><p><br/></p><p> 3、订阅号适合消息类,新闻类应用,常常需要推送文章给用户的;服务号适合自助查询等。</p><p><br/></p><p> 4、订阅号被认证后也享用自定义菜单等功能,仍旧是300元/每年</p><p><br/></p><p><br/></p><p><br/></p><p>二、专业术语:</p><p><br/></p><p> 1、OpenId:微信服务器并不会告诉公众号用户的微信ID,即使是你的关注者也不行,为了解决开发中唯一标识的问题,微信使用了OpenId,所谓的OpenId,就是用户和微信公众号之间的一种唯一关系。一个用户在一个公众号面前,享用唯一的OpenId,不会和别人重复。换言之,同一个用户在另一个公众号面前,是拥有另一个OpenId的。再直白些就是$openId = md5('用户微信ID+公众号ID')</p><p><br/></p><p> 2、Access_Token:此项只有认证号的功能才会使用的到,Access_token是一个授权标识,即一个授权验证码,一个标识10分钟内有效,10分钟的有效期内公众号的多个关注者可以使用同一个Access_Token。在使用主动给指定用户发送消息、自定义菜单、用户管理和用户组管理等功能的时候,每次操作需要给微信服务器以参数的形式附带Access_token。</p><p><br/></p><p> 3、Access_Token网页版:本Access_Token网页版授权时会使用到,和2中的Access_Toekn是不同的东西,不过使用我们的LaneWeChat微信快速开发框架是不需要了解这些的。Access_Token网页版是说在用户打开你的公众号提供的网页的时候,你的网页需要获取用户的OpenId、昵称、头像等信息的时候授权用的。同时,本Access_Token网页版有两种用法,一种是打开网页后弹出一个授权框,让用户点击是否授权,界面像主流的开放平台授权界面(比如QQ登陆某网站,支付宝账号登陆某网站等);另一种是不需要弹出授权框仍旧可以获取用户信息,用法可以在实例中看到。</p><p><br/></p><p><br/></p><p>如何安装:</p><p><br/></p><p> 1、本框架以代码包的插件形式放在项目的目录中即可。</p><p><br/></p><p> 2、配置项:打开根目录下的config.php,修改定义常量WECHAT_APPID,WECHAT_APPSECRET,WECHAT_URL。其中前两项可以在微信公众号官网的开发者页面中找到,而WECHAT_URL是你微信项目的URL,以http://开头</p><p><br/></p><p> 3、本框架的唯一入口为根目录下的wechat.php</p><p><br/></p><p> 4、首次使用时,请打开根目录下的wechat.php,注释掉20行,21行,并且打开注释第24行。</p><p><br/></p><p> 5、在微信开发者-填写服务器配置页面,填写URL为http://www.lanecn.com/wechat.php,保证该URL可以通过80端口正常访问(微信服务器目前只支持80端口),并且将Token填写为config.php中的WECHAT_TOKEN常量的内容(可以修改)。</p><p><br/></p><p> 6、微信服务器在第4步验证通过后,反向操作第4步,即注释掉第24行,打开注释第20行,21行。至此,安装配置完成。</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>初出茅庐:</p><p><br/></p><p> 1、给你的微信公众号发送一条文本消息,比如hello world或者其他什么的。这个时候你应该会收到一条“收到文本”的服务器反馈的被动响应的消息。</p><p><br/></p><p> 2、这个时候你需要先为自己鼓掌。</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>流程分析:</p><p><br/></p><p> 1、我们给微信服务器发送了一条“hello world”的文本消息。</p><p><br/></p><p> 2、微信服务器收到我们的消息后,查找该公众账号所配置的服务器信息中的URL(如何安装部分 - 第5步)。</p><p><br/></p><p> 3、微信服务器向第二步获取的URL发送请求,参数是微信服务器自己拼接过的XML格式。</p><p><br/></p><p> 4、根目录下的wechat.php,引入了我们的配置文件和所需的类后,进入了类WeChat的方法run()。该类位于core/wechat.lib.php。微信的XML数据此时已经被解析为数组,变量名为$request。</p><p><br/></p><p> 5、然后,我们进入了类WechatRequest的方法switchType(),根据不同的消息类型,给予不同的响应。比如用户发送文本消息和关注事件,给出的返回应该是不同的。当然,你要给出同样的提示也不能说是错的。</p><p><br/></p><p> 6、在第5步中的方法中,是一个switch,根据消息类型(此时是文本类型,微信服务器给我的是text)选择了一个处理文本消息的方法,类WechatRequest中的方法text()。该方法的功能是发送文本消息,文本内容是“收到文本”。</p><p><br/></p><p> 7、此时,我们return了一个数据返回给了上层调用,层层return,就到了我们根目录的下的唯一入口文件wechat.php,此时我们返回的数据被echo出来了。</p><p><br/></p><p> 8、微信服务器拿到了输出的数据,微信服务器进行分析和处理,将文本发送给了用户的微信客户端。我们就在手机上看到了微信输出的“收到文本”。</p><p><br/></p><p> 9、流程结束,这就是发送“hello world”,然后返回给用户“收到文本”。</p><p><br/></p><p><br/></p><p>牛刀小试:</p><p><br/></p><p> 1、打开core/wechatrequest.php文件,讲方法text()中的变量修改为$content = '收到文本消息'.$request['content'];</p><p><br/></p><p> 2、保存并且上传到你的服务器。</p><p><br/></p><p> 3、在微信中打开你的公众号,输入文本消息“hello world”。见证奇迹的时刻到了。这个时候你的手机微信客户端中现实的是“收到文本消息hello world”。</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>函数详解:</p><p><br/></p><p> 一、被动给用户发送消息。</p><p><br/></p><p> 1、类简介:用户输入文本、图片、语音、音乐、视频等消息,以及关注、取消关注,上报地理位置等事件后,服务器被动给出应答。</p><p><br/></p><p> 2、使用命名空间:use LaneWeChat\\Core\\ResponsePassive;</p><p><br/></p><p> 3、参数: $fromusername = "谁发给你的?(用户的openId)" 在变量$request['fromusername']中</p><p><br/></p><p> $tousername = "你的公众号Id"; 在变量$require['tousername']中</p><p><br/></p><p> $mediaId = "通过上传多媒体文件,得到的id。";</p><p><br/></p><p> 4、发送文本</p><p><br/></p><p> ResponsePassive::text($fromusername, $tousername, '文本消息内容');</p><p><br/></p><p> 5、发送图片</p><p><br/></p><p> ResponsePassive::image($fromusername, $tousername, $mediaId);</p><p><br/></p><p> 6、发送语音</p><p><br/></p><p> ResponsePassive::voice($fromusername, $tousername, $mediaId);</p><p><br/></p><p> 7、发送视频</p><p><br/></p><p> ResponsePassive::video($fromusername, $tousername, $mediaId, '视频标题', '视频描述');</p><p><br/></p><p> 8、发送音乐</p><p><br/></p><p> ResponsePassive::music($fromusername, $tousername, '音乐标题', '音乐描述', '音乐链接', '高质量音乐链接,WIFI环境优先使用该链接播放音乐', '缩略图的媒体id,通过上传多媒体文件,得到的id');</p><p><br/></p><p> 9、发送图文</p><p><br/></p><p> 1)创建图文消息内容</p><p><br/></p><p> $tuwenList = array();</p><p><br/></p><p> $tuwenList[] = array('title'=>'标题1', 'description'=>'描述1', 'pic_url'=>'图片URL1', 'url'=>'点击跳转URL1');</p><p><br/></p><p> $tuwenList[] = array('title'=>'标题2', 'description'=>'描述2', 'pic_url'=>'图片URL2', 'url'=>'点击跳转URL2');</p><p><br/></p><p> 2)构建图文消息格式</p><p><br/></p><p> $itemList = array();</p><p><br/></p><p> foreach($tuwenList as $tuwen){</p><p><br/></p><p> $itemList[] = ResponsePassive::newsItem($tuwen['title'], $tuwen['description'], $tuwen['pic_url'], $tuwen['url']);</p><p><br/></p><p> }</p><p><br/></p><p> 3)发送图文消息</p><p><br/></p><p> ResponsePassive::news($fromusername, $tousername, $itemList);</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>二、AccessToken授权。</p><p><br/></p><p> 1、类简介:除了被动相应用户之外,在主动给用户发送消息,用户组管理等高级操作,是需要AccessToken授权的,我们调用一个URL给微信服务器,微信服务器会返回给我们一个散列字符串,在高级操作的时候需要将此串以参数的形式发送。散列字符串10分钟内有效,过期需要重新获取,获取新的后之前的全部失效。</p><p><br/></p><p> 2、使用命名空间:use LaneWeChat\\Core\\AccessToken;</p><p><br/></p><p> 3、参数:无</p><p><br/></p><p> 4、获取AccessToken</p><p><br/></p><p> AccessToken::getAccessToken(); 该调用会返回微信服务器散列后的AccessToken字符串。</p><p><br/></p><p> 5、温馨提示</p><p><br/></p><p> 如果暂且用不到此功能,请跳过。最后来看这里!</p><p><br/></p><p> 6、功能补充</p><p><br/></p><p> 有一个地方需要用户自行完善,根据介绍我们已经知道了,获取AccessToken只有10分钟的有效期,过期需要重新获取。因此,我们需要存储这个AccessToken。</p><p><br/></p><p> 由于大家的存储方式各不相同,有Mysql的,有Redis的,有MongoDB的,还有Session的。所以这里我讲存储和读取给留空了。</p><p><br/></p><p> 流程:AccessToken类,public方法只有一个,就是getAccessToken()。这个方法会调用一个私有方法_checkAccessToken()来检测AccessToken是否存在并且是否过期,如果不存在或过期,则调用私有方法_getAccessToken()</p><p><br/></p><p> 完善步骤:</p><p><br/></p><p> 1)、打开core/accesstoken.lib.php文件。</p><p><br/></p><p> 2)、私有方法_getAccessToken()的倒数第二行(return是倒数第一行),在这个地方,请讲变量$accessTokenJson存储起来,变量$accessTokenJson是一个字符串。</p><p><br/></p><p> 3)、私有方法_checkAccessToken()的第一行就是读取操作(有一行伪代码$accessToken = YourDatabase::get('access_token');),将刚才第二步的存储的东西给读出来,并且赋值给$accessToken。</p><p><br/></p><p> 4)、在第二步的存储,第三部的读取的时候,请不要修改数据,仅仅完善一个读和存的操作就可以了。</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>三、主动给用户发送消息。<br/></p><p><br/></p><p> 1、类简介:用户输入文本、图片、语音、音乐、视频等消息,以及关注、取消关注,上报地理位置等事件后,服务器被动给出应答。</p><p><br/></p><p> 2、使用命名空间:use LaneWeChat\\Core\\ResponsePassive;</p><p><br/></p><p> 3、参数 $tousername = "你的公众号Id"; 在变量$require['tousername']中</p><p><br/></p><p> $mediaId = "通过上传多媒体文件,得到的id。";</p><p><br/></p><p> 4、发送文本内容</p><p><br/></p><p> ResponseInitiative::text($tousername, '文本消息内容');</p><p><br/></p><p> 5、发送图片</p><p><br/></p><p> ResponseInitiative::image($tousername, $mediaId);</p><p><br/></p><p> 6、发送语音</p><p><br/></p><p> ResponseInitiative::voice($tousername, $mediaId);</p><p><br/></p><p> 7、发送视频</p><p><br/></p><p> ResponseInitiative::video($tousername, $mediaId, '视频描述', '视频标题');</p><p><br/></p><p> 8、发送地理位置</p><p><br/></p><p> ResponseInitiative::music($tousername, '音乐标题', '音乐描述', '音乐链接', '高质量音乐链接,WIFI环境优先使用该链接播放音乐', '缩略图的媒体id,通过上传多媒体文件,得到的id');</p><p><br/></p><p> 9、发送图文消息</p><p><br/></p><p> 1)创建图文消息内容</p><p><br/></p><p> $tuwenList = array();</p><p><br/></p><p> $tuwenList[] = array('title'=>'标题1', 'description'=>'描述1', 'pic_url'=>'图片URL1', 'url'=>'点击跳转URL1');</p><p><br/></p><p> $tuwenList[] = array('title'=>'标题2', 'description'=>'描述2', 'pic_url'=>'图片URL2', 'url'=>'点击跳转URL2');</p><p><br/></p><p> 2)构建图文消息格式</p><p><br/></p><p> $itemList = array();</p><p><br/></p><p> foreach($tuwenList as $tuwen){</p><p><br/></p><p> $itemList[] = ResponseInitiative::newsItem($tuwen['title'], $tuwen['description'], $tuwen['pic_url'], $tuwen['url']);</p><p><br/></p><p> }</p><p><br/></p><p> 3)发送图文消息</p><p><br/></p><p> ResponseInitiative::news($tousername, $itemList);</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>四、用户及用户组管理。</p><p><br/></p><p> 1、类简介:获取粉丝列表,创建\\修改用户组,讲用户添加\\移除到用户组。</p><p><br/></p><p> 2、使用命名空间:use LaneWeChat\\Core\\UserManage;</p><p><br/></p><p> 3、参数 $openId = '用户和微信公众号的唯一ID'; 在变量$require['openid']中</p><p><br/></p><p> $mediaId = "通过上传多媒体文件,得到的id。";</p><p><br/></p><p> $groupId = '分组ID'; 在添加新分组、获取分组列表的时候可以得到</p><p><br/></p><p> 4、分组管理 - 创建分组</p><p><br/></p><p> UserManage::createGroup('分组名');</p><p><br/></p><p> 5、分组管理 - //获取分组列表</p><p><br/></p><p> UserManage::getGroupList();</p><p><br/></p><p> 6、分组管理 - 查询用户所在分组</p><p><br/></p><p> UserManage::getGroupByOpenId($openId);</p><p><br/></p><p> 7、分组管理 - 修改分组名</p><p><br/></p><p> UserManage::editGroupName($groupId, '新的组名');</p><p><br/></p><p> 8、分组管理 - 移动用户分组</p><p><br/></p><p> UserManage::editUserGroup($openId, $groupId);</p><p><br/></p><p> 9、用户管理 - 获取用户基本信息</p><p><br/></p><p> UserManage::getUserInfo($openId);</p><p><br/></p><p> 10、用户管理 - 获取关注者列表</p><p><br/></p><p> UserManage::getFansList($next_openId='');</p><p><br/></p><p> 11、用户管理 - 获取网络状态</p><p><br/></p><p> UserManage::getNetworkState();</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>五、网页授权。</p><p><br/></p><p> 1、类简介:在网页中获取来访用户的数据。</p><p><br/></p><p> 2、使用命名空间:use LaneWeChat\\Core\\WeChatOAuth;</p><p><br/></p><p> 3、参数 $openId = '用户和微信公众号的唯一ID'; 在变量$require['openid']中</p><p><br/></p><p> $mediaId = "通过上传多媒体文件,得到的id。";</p><p><br/></p><p> $groupId = '分组ID'; 在添加新分组、获取分组列表的时候可以得到</p><p><br/></p><p> 4、获取CODE。</p><p><br/></p><p> 参数:$scope:snsapi_base不弹出授权页面,只能获得OpenId;snsapi_userinfo弹出授权页面,可以获得所有信息</p><p><br/></p><p> 参数:$redirect_uri:将会跳转到redirect_uri/?code=CODE&state=STATE 通过GET方式获取code和state。获取CODE时,发送请求和参数给微信服务器,微信服务器会处理后将跳转到本参数指定的URL页面</p><p><br/></p><p> WeChatOAuth::getCode($redirect_uri, $state=1, $scope='snsapi_base');</p><p><br/></p><p> 5、通过code换取网页授权access_token(access_token网页版)。首先请注意,这里通过code换取的网页授权access_token,与基础支持中的access_token不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。</p><p><br/></p><p> 参数:$code getCode()获取的code参数。$code = $_GET['code'];</p><p><br/></p><p> WeChatOAuth::getAccessTokenAndOpenId($code);</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>六、多媒体上传下载</p><p><br/></p><p> 1、类简介:在网页中获取来访用户的数据。上传的多媒体文件有格式和大小限制,如下:</p><p><br/></p><p> * 图片(image): 1M,支持JPG格式</p><p><br/></p><p> * 语音(voice):2M,播放长度不超过60s,支持AMR\\MP3格式</p><p><br/></p><p> * 视频(video):10MB,支持MP4格式</p><p><br/></p><p> * 缩略图(thumb):64KB,支持JPG格式</p><p><br/></p><p> * 媒体文件在后台保存时间为3天,即3天后media_id失效</p><p><br/></p><p> 2、使用命名空间:use LaneWeChat\\Core\\Media;</p><p><br/></p><p> 3、参数 $filename 上传的文件的绝对路径</p><p><br/></p><p> $type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)</p><p><br/></p><p> $mediaId = "通过上传多媒体文件,得到的id。";</p><p><br/></p><p> $groupId = '分组ID'; 在添加新分组、获取分组列表的时候可以得到</p><p><br/></p><p> 4、上传:上传后,微信服务器会返回一个mediaId。</p><p><br/></p><p> Media::upload($filename, $type);</p><p><br/></p><p> 5、下载:根据mediaId下载一个多媒体文件。</p><p><br/></p><p> Media::download($mediaId);</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>七、自定义菜单</p><p><br/></p><p> 1、类简介:添加自定义菜单。最多可以有三个一级菜单,每个一级菜单最多可以有五个菜单。一级菜单最多4个汉字,二级菜单最多7个汉字。创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来。建议测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。</p><p><br/></p><p> 摘自微信官方网站:目前自定义菜单接口可实现两种类型按钮,如下:</p><p><br/></p><p> click:</p><p><br/></p><p> 用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者,并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;</p><p><br/></p><p> view:</p><p><br/></p><p> 用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url值<span class="Apple-tab-span" style="white-space:pre"></span>(即网页链接),达到打开网页的目的,建议与网页授权获取用户基本信息接口结合,获得用户的登入个人信息。</p><p><br/></p><p> 总结一下哦,就是微信的菜单分两种,一种是view型,就是你设置一个网址,点了这个菜单之后就跳到你设置的网址去了。另一种就是click型,你设置一个key,然后用户点击的时候会通过本框架唯一入口wechat.php发送一个消息类型为event的请求,在wechatrequest.lib.php文件下的eventClick方法中可以使用。</p><p><br/></p><p> 2、使用命名空间:use LaneWeChat\\Core\\Menu;</p><p><br/></p><p> 3、设置菜单:是所有的菜单数据全部发送一次,可不是每新增一个只发一个菜单。</p><p><br/></p><p> Menu::setMenu($menuList);</p><p><br/></p><p> $menuLis 是菜单列表,结构如下:</p><p><br/></p><p> $menuList = array(</p><p><br/></p><p> array('id'=>'1', 'pid'=>'0', 'name'=>'顶级分类一', 'type'=>'', 'code'=>''),</p><p><br/></p><p> array('id'=>'2', 'pid'=>'1', 'name'=>'分类一子分类一', 'type'=>'2', 'code'=>'lane_wechat_menu_1_1'),</p><p><br/></p><p> array('id'=>'3', 'pid'=>'1', 'name'=>'分类一子分类二', 'type'=>'1', 'code'=>'http://www.lanecn.com'),</p><p><br/></p><p> array('id'=>'4', 'pid'=>'0', 'name'=>'顶级分类二', 'type'=>'1', 'code'=>'http://www.php.net/'),</p><p><br/></p><p> array('id'=>'5', 'pid'=>'0', 'name'=>'顶级分类三', 'type'=>'2', 'code'=>'lane_wechat_menu_3'),</p><p><br/></p><p> );</p><p><br/></p><p> 'id'是您的系统中对分类的唯一编号;</p><p><br/></p><p> 'pid'是该分类的上级分类,顶级分类则填写0;</p><p><br/></p><p> 'name'是分类名称;</p><p><br/></p><p> 'type'是菜单类型,数字1或者2,1是view类型,2是click类型,如果该分类下有子分类请务必留空;</p><p><br/></p><p> 'code'是view类型的URL或者click类型的自定义key,如果该分类下有子分类请务必留空。</p><p><br/></p><p> 4、获取微信菜单:获取到的是已经设置过的菜单列表,格式为Json,是微信服务器返回的原始数据。</p><p><br/></p><p> Menu::getMenu();</p><p><br/></p><p> 5、删除微信菜单:将会删除设置过的所有菜单(一键清空)。</p><p><br/></p><p> Menu::delMenu();</p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>实例示范:<br/></p><p><br/></p><p> 1、通过网页授权获得用户信息</p><p><br/></p><p> 场景:用户点击了我的自定义菜单,或者我发送的文本消息中包含一个URL,用户打开了我的微信公众号的网页版,我需要获取用户的信息。</p><p><br/></p><p> 代码:</p><p><br/></p><p> <?php</p><p> use LaneWeChat\\Core\\WeChatOAuth;</p><p> use LaneWeChat\\Core\\UserManage;</p><p><br/></p><p> //第一步,获取CODE</p><p> WeChatOAuth::getCode('http://www.lanecn.com/index.php', 1, 'snsapi_base');</p><p> //此时页面跳转到了http://www.lanecn.com/index.php,code和state在GET参数中。</p><p> $code = $_GET['code'];</p><p> //第二步,获取access_token网页版</p><p> $openId = WeChatOAuth::getAccessTokenAndOpenId($code);</p><p> //第三步,获取用户信息</p><p> $userInfo = UserManage::getUserInfo($openId['openid']);</p><p> ?></p><p><br/></p><p> 2、更多实例正在补充。<br/></p>', 1407137982, 33, 4, 1),
(66, 1, '李轩Lane', 'LaneWeChat新版本V1.2发布,新增自定义菜单', 'PHP微信快速开发框架LaneWeChat新版本V1.2发布于2014-08-17,新增自定义菜单的管理,包括添加新菜单,清空所有菜单,获取菜单列表等', 'PHP微信快速开发框架LaneWeChat新版本V1.2发布,新增自定义菜单', 'PHP微信快速开发框架LaneWeChat新版本V1.2发布,新增自定义菜单', 'LaneWeChat_PHP_微信开发框架', 'LaneWeChat|PHP|微信', 3005, '<p>PHP微信快速开发框架LaneWeChat新版本V1.2发布于2014-08-17,新增自定义菜单的管理,包括添加新菜单,清空所有菜单,获取菜单列表等。</p><p><br/></p><p>新增:</p><p>一、自定义菜单</p><p> 1、自定义菜单的管理。</p><p> 2、包括添加新菜单。</p><p> 3、清空所有菜单。</p><p> 4、获取菜单列表。</p><p>二、多媒体上传与下载(获取Media_id的途径)</p><p> 1、多媒体上传<br/></p><p> 2、多媒体下载<br/></p><p><br/></p><p>详细文档:<a href="http://www.lanecn.com/article/main/aid-65" _src="http://www.lanecn.com/article/main/aid-65">http://www.lanecn.com/article/main/aid-65</a></p><p><br/></p>', 1408343919, 4, 0, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(67, 1, '李轩Lane', 'CoreSeek Mysql 安装与测试 For Mac OS X(中文分词与中文全文检索)', 'CoreSeek安装比较麻烦,官方手册对此的支持并不算很好。CoreSeek是基于Sphinx的中文的分词和全文检索软件。本文是在MAC OS X系统下安装和调试CoreSeek。', 'CoreSeek安装_Coreseek测试_coreseek Mac OS X', 'CoreSeek安装比较麻烦,官方手册对此的支持并不算很好。CoreSeek是基于Sphinx的中文的分词和全文检索软件。本文是在MAC OS X系统下安装和调试CoreSeek。', 'CoreSeek安装,Coreseek测试,coreseek Mac OS X', 'PHP|CoreSeek|MAC', 1312, '<p> CoreSeek安装比较麻烦,官方手册对此的支持并不算很好。CoreSeek是基于Sphinx的中文的分词和全文检索软件。本文是在MAC OS X系统下安装和调试CoreSeek。</p><p> 安装过程中报错如果是警告warning则忽略,如果是错误error,则必须要处理。</p><p> CoreSeek是支持三种数据来源的,一种是众所周知的Mysql,一种是XML文件,另一种是Python。而Python则是万能数据类型。在本CoreSeek安装测试教程中只示例数据源是XML文件和MYSQL。</p><p><br/></p><p> 官方手册地址:http://www.coreseek.cn/products-install/install_on_macosx</p><p><br/></p><p> 一、设置环境变量<br/></p><p>[code]<br/></p><p>$ export PATH=/usr/local/bin:$PATH</p><p>$ export LC_ALL=zh_CN.UTF-8</p><p>$ export.UTF-8</p><p>[/code]</p><p><br/></p><p> 二、安装依赖库:m4、autoconf、automake、libtool。</p><p>注意:不要brew install 来安装,因为对安装的库的版本有要求。</p><p>[code]</p><p>$ curl -O -L http://mirrors.kernel.org/gnu/m4/m4-1.4.13.tar.gz</p><p>$ tar -xzvf m4-1.4.13.tar.gz</p><p>$ cd m4-1.4.13</p><p>$ sudo ./configure --prefix=/usr/local/opt</p><p>$ sudo make</p><p>$ sudo make install</p><p>$ cd ..</p><p><br/></p><p>$ curl -O -L http://mirrors.kernel.org/gnu/autoconf/autoconf-2.65.tar.gz</p><p>$ tar -xzvf autoconf-2.65.tar.gz</p><p>$ cd autoconf-2.65</p><p>$ sudo ./configure --prefix=/usr/local/opt</p><p>$ sudo make</p><p>$ sudo make install</p><p>$ cd ..</p><p><br/></p><p>$ curl -O -L http://mirrors.kernel.org/gnu/automake/automake-1.11.tar.gz</p><p>$ tar xzvf automake-1.11.tar.gz</p><p>$ cd automake-1.11</p><p>$ ./configure --prefix=/usr/local/opt</p><p>$ sudo make</p><p>$ sudo make install</p><p>$ cd ..</p><p><br/></p><p>$ curl -O -L http://mirrors.kernel.org/gnu/libtool/libtool-2.2.6b.tar.gz</p><p>$ tar xzvf libtool-2.2.6b.tar.gz</p><p>$ cd libtool-2.2.6b</p><p>$ sudo ./configure --prefix=/usr/local/opt</p><p>$ sudo make</p><p>$ sudo make install</p><p>$ cd ..</p><p>[/code]</p><p><br/></p><p> 三、安装Mysql。</p><p>1、mysql 的安装自行安装</p><p>2、查找mysql头文件地址和库文件地址。我用</p><p>[code]</p><p>brew install mysql</p><p>[/code]</p><p>安装的Mysql,头文件地址和库文件地址分别是/usr/local/Cellar/mysql/5.6.17_1/include/mysql 和 /usr/local/Cellar/mysql/5.6.17_1/lib。</p><p>头文件地址就是mysql.h所在的目录,库文件地址就是libmysqlclient.a所在的目录。</p><p><br/></p><p> 四、下载Coreseek。</p><p>[code]</p><p>$ curl -O -L http://www.coreseek.cn/uploads/csft/3.2/coreseek-3.2.14.tar.gz</p><p>$ tar xzvf coreseek-3.2.14.tar.gz</p><p>$ cd coreseek-3.2.14</p><p>[/code]</p><p>在coreseek-3.2.14文件夹下有mmseg和csft和testpack。mmseg是分词服务,csft是CoreSeek的核心服务,testpack是测试用例。</p><p><br/></p><p> 五、安装mmseg</p><p>[code]</p><p>$ cd mmseg-3.2.14</p><p>$ sudo ./bootstrap</p><p>$ sudo ./configure --prefix=/usr/local/opt/mmseg3</p><p>$ sudo make</p><p>$ sudo make install</p><p>$ cd ..</p><p>[/code]</p><p>在make的时候,可能会报错,如下</p><p>file included from css/ThesaurusDict.cpp:6:</p><p>../src/css/ThesaurusDict.h:12:17: error: expected namespace name</p><p>using namespace __gnu_cxx;</p><p>^</p><p>css/ThesaurusDict.cpp:79:15: warning: result of comparison against a string</p><p>literal is unspecified (use strncmp instead) [-Wstring-compare]</p><p>if (filename == "-") {</p><p> ^ ~~~</p><p> css/ThesaurusDict.cpp:116:15: warning: result of comparison against a string</p><p>literal is unspecified (use strncmp instead) [-Wstring-compare]</p><p>if (filename != "-") {</p><p> ^ ~~~</p><p> 2 warnings and 1 error generated.</p><p> make[2] : *** [ThesaurusDict.lo] Error 1</p><p>make[1]: *** [install-recursive] Error 1</p><p><br/></p><p>这个时候make进程已经终止。原因是因为<span style="color: rgb(54, 46, 43); font-family: Arial; font-size: 14px; line-height: 26px; background-color: rgb(255, 255, 255);">编译器版本太高导致的,</span>修改方法:1是降低编译器版本,反正我打死也不愿意。方法2如下:</p><p>[code]</p><p>vim src/css/ThesaurusDict.h</p><p>###在头部找到:#include <string></p><p>###再其下加入一行代码:</p><p>#include <ext/hash_map></p><p>[/code]</p><p>修改完后保存退出,继续重新sudo make一下,就没有error级错误了,然后sudo make install即可。</p><p><br/></p><p> 六、安装coreseek</p><p>[code]</p><p>$ cd csft-3.2.14</p><p>$ sudo sh buildconf.sh</p><p>$ sudo ./configure --prefix=/usr/local/opt/coreseek --without-unixodbc --with-mmseg --with-mmseg-includes=/usr/local/opt/mmseg3/include/mmseg/ --with-mmseg-libs=/usr/local/opt/mmseg3/lib/ --with-mysql --with-mysql-includes=/usr/local/Cellar/mysql/5.6.17_1/include/mysql --with-mysql-libs=/usr/local/Cellar/mysql/5.6.17_1/lib</p><p>$ sudo make</p><p>$ sudo make install</p><p>$ cd ..</p><p>[/code]</p><p>在./configure时,参数--with-mysql-includes是mysql头文件位置,--with-mysql-libs是mysql库文件位置,请在本CoreSeek安装教程第三步所记录的mysql头文件地址和库文件地址,替换。</p><p><br/></p><p>在make时,可能又会出现error级的错误,如果出现make程序是停止运行的,必须要修改。错误提示如下:</p><p>phinxexpr.cpp:1047:11: error: use of undeclared identifier 'ExprEval'</p><p>T val = ExprEval ( this->m_pArg, tMatch ); // 'this' fixes gcc ...</p><p><br/></p><p>解决方法:修改源代码。</p><p>[code]</p><p>vim src/sphinxexpr.cpp</p><p>[/code]</p><p>将T val = ExprEval( this->m_pArg, tMatch )替换为T val = this->ExprEval ( this->m_pArg, tMatch )。</p><p>就是加了个“this->”,是把这个文件中所有的ExprEval()函数都修改了,有三四个吧。</p><p>修改后保存退出,重新sudo make,然后sudo make install即可。</p><p><br/></p><p> 七、测试XML数据</p><p>[code]</p><p>$ cd testpack</p><p><br/></p><p>#测试编码是否正确显示中文,如果不是中文则请看本CoreSeek安装测试教程第一步</p><p>$ cat var/test/test.xml</p><p><br/></p><p># 测试mmseg分词的效果</p><p>$ /usr/local/opt/mmseg3/bin/mmseg -d /usr/local/opt/mmseg3/etc var/test/test.xml </p><p><br/></p><p># 建立检索的索引。</p><p>$ /usr/local/opt/coreseek/bin/indexer -c etc/csft.conf --all</p><p><br/></p><p>#全文搜索“网络搜索”</p><p>$ /usr/local/opt/coreseek/bin/search -c etc/csft.conf 网络搜索</p><p>[/code]</p><p>如果在建立检索的索引出错,FATAL: failed to lock var/data/xml.spl: Resource temporarily unavailable, will not index. Try --rotate option.</p><p>则修改为</p><p>[code]</p><p>$ /usr/local/opt/coreseek/bin/indexer -c etc/csft.conf --all --rotate</p><p>[/code]</p><p><br/></p><p><br/></p><p> 八、测试MYSQL数据源</p><p>[code]</p><p>cd testpack</p><p>[/code]</p><p><br/></p><p>1、修改配置文件,文件位于testpack/etc/csft_mysql.conf</p><p>[code]</p><p>vim etc/csft_mysql.conf</p><p>[/code]</p><p>我的csft_mysql.conf文件如下:记得把mysql 的sql_host,sql_user,sql_pass,sql_db,sql_port修改为自己的,并且把路劲都修改为你自己的路径。</p><p>[code]</p><p>#MySQL数据源配置,详情请查看:http://www.coreseek.cn/products-install/mysql/</p><p>#请先将var/test/documents.sql导入数据库,并配置好以下的MySQL用户密码数据库</p><p><br/></p><p>#源定义</p><p>source mysql</p><p>{</p><p> type = mysql</p><p><br/></p><p> sql_host = localhost</p><p> sql_user = root</p><p> sql_pass = 8823150</p><p> sql_db = test</p><p> sql_port = 3306</p><p> sql_query_pre = SET NAMES utf8</p><p><br/></p><p> sql_query = SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, title, content FROM documents</p><p> #sql_query第一列id需为整数</p><p> #title、content作为字符串/文本字段,被全文索引</p><p> sql_attr_uint = group_id #从SQL读取到的值必须为整数</p><p> sql_attr_timestamp = date_added #从SQL读取到的值必须为整数,作为时间属性</p><p><br/></p><p> sql_query_info_pre = SET NAMES utf8 #命令行查询时,设置正确的字符集</p><p> sql_query_info = SELECT * FROM documents WHERE id=$id #命令行查询时,从数据库读取原始数据信息</p><p>}</p><p><br/></p><p>#index定义</p><p>index mysql</p><p>{</p><p> source = mysql #对应的source名称</p><p> path = /Users/lane/coreseek-3.2.14/testpack/var/data/mysql #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...</p><p> docinfo = extern</p><p> mlock = 0</p><p> morphology = none</p><p> min_word_len = 1</p><p> html_strip = 0</p><p><br/></p><p> #中文分词配置,详情请查看:http://www.coreseek.cn/products-install/coreseek_mmseg/</p><p> charset_dictpath = /usr/local/opt/mmseg3/etc/ #BSD、Linux环境下设置,/符号结尾</p><p> #charset_dictpath = etc/ #Windows环境下设置,/符号结尾,最好给出绝对路径,例如:C:/usr/local/coreseek/etc/...</p><p> charset_type = zh_cn.utf-8</p><p>}</p><p><br/></p><p>#全局index定义</p><p>indexer</p><p>{</p><p> mem_limit = 128M</p><p>}</p><p><br/></p><p>#searchd服务定义</p><p>searchd</p><p>{</p><p> listen = 9312</p><p> read_timeout = 5</p><p> max_children = 30</p><p> max_matches = 1000</p><p> seamless_rotate = 0</p><p> preopen_indexes = 0</p><p> unlink_old = 1</p><p> pid_file = /Users/lane/coreseek-3.2.14/testpack/var/log/searchd_mysql.pid #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...</p><p> log = /Users/lane/coreseek-3.2.14/testpack/var/log/searchd_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...</p><p> query_log = /Users/lane/coreseek-3.2.14/testpack/var/log/query_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...</p><p>}</p><p>[/code]</p><p><br/></p><p>2、给mysql导入测试数据</p><p> mysql的测试数据我们用的是test数据库,documents数据表。请自行确保test数据库存在,我们一起来建documents表,这个表的结构和数据都是由CoreSeek提供的,在testpack/var/test/documents.sql</p><p>[code]</p><p>mysql -u root -p</p><p>#输入密码</p><p><br/></p><p>#看看,数据库test在不在</p><p>mysql > show databases; </p><p>#如果test库不在请创建</p><p>mysql > create database test;</p><p><br/></p><p>use test;</p><p><br/></p><p>#导入数据</p><p>source /Users/lane/coreseek-3.2.14/testpack/var/test/documents.sql</p><p>[/code]</p><p><br/></p><p>3、测试:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">$ cd testpack</p><p style="white-space: normal;"><br/></p><p style="white-space: normal;"># 建立检索的索引</p><p style="white-space: normal;">$ /usr/local/opt/coreseek/bin/indexer -c etc/csft_mysql.conf --all</p><p style="white-space: normal;"><br/></p><p style="white-space: normal;">#全文搜索“网络搜索”</p><p style="white-space: normal;">$ /usr/local/opt/coreseek/bin/search -c etc/csft_mysql.conf 网络搜索</p><p style="white-space: normal;">[/code]</p><p style="white-space: normal;">如果提示有错误,请检查csft_mysql.conf的路径、mysql的配置等信息是否正确。</p><p><br/></p><p><br/></p><p> 九、测试PHP+MYSQL</p><p>1、先启动服务</p><p>[code]</p><p>/usr/local/opt/coreseek/bin/searchd -c etc/csft.conf</p><p>[/code]</p><p>2、PHP文件</p><p>[code]</p><p><?php</p><p>require ( "/Users/lane/coreseek-3.2.14/testpack/api/sphinxapi.php" );</p><p><br/></p><p>$cl = new SphinxClient ();</p><p>$cl->SetServer ( '127.0.0.1', 9312);</p><p>$cl->SetConnectTimeout ( 3 );</p><p>$cl->SetArrayResult ( true );</p><p>$cl->SetMatchMode ( SPH_MATCH_ANY);</p><p>$res = $cl->Query ( '网络搜索', "*" );</p><p>print_r($cl);</p><p>print_r($res);</p><p>[/code]</p><p><br/></p><p><br/></p><p>我在Linux折腾了一天没有搞定,在MAC搞了半天搞定了。等搞定Linux后再发Linux的。</p>', 1409639263, 1, 0, 0),
(68, 11, '李轩Lane', '如何安全的存储密码?', '如何安全的存储密码?携程信用卡信息泄漏,支付宝密码泄漏,CSDN数据库泄漏,我们的密码已经越来越不安全了,存储密码是一个系统最基本的功能,安全的存储密码是一个系统对用户最基本的责任。', '存储密码_密码存储_安全存储密码', '如何安全的存储密码?携程信用卡信息泄漏,支付宝密码泄漏,CSDN数据库泄漏,我们的密码已经越来越不安全了,存储密码是一个系统最基本的功能,安全的存储密码是一个系统对用户最基本的责任。', '存储密码,密码存储,安全存储密码', '哈希|hash|加密', 1108, '<p>一、我是新手我怕谁</p><p><br/></p><p> 新手程序猿通常会直接存储明文密码在数据库中,好一点的会使用MD5来加密密码后存储md5(password),再好一点的会sha1加密密码后存储sha1(password)。将常用的组合哈希后存入数据库,用来爆库,这个就是所谓的彩虹表。</p><p><br/></p><p>二、加盐salted</p><p><br/></p><p> 在密码中加入随机数字或字符,然后再进行哈希,看起来叼了很多,但是实际上对于现在计算机来说,即使简单的使用了盐和哈希的加密,短密码仍然会在非常短的情况下就会被破解出来。</p><p><br/></p><p>三、美国标准</p><p><br/></p><p> 美国政府的标准,已经用于政府和军方的系统。PBKDF2加密算法,全程是Password-Based Key Derivation Function。</p><p> PBKDF2加密算法就是牺牲了时间来换取安全,一个明文的密码+随机的盐,然后哈希散列加密后存储起来,这是我们前面说的(二、加盐salted)。把这个过程重复100次,得到的结果存储起来。</p><p> 请注意,尽管我们说牺牲了时间,又说到了重复100次,那也是很快的,因为我们的普通服务器单次的运算都是在毫秒级。</p><p><br/></p><p>四、scrypt</p><p><br/></p><p> scrypt是由著名的FreeBSD黑客 Colin Percival为他的备份服务 Tarsnap开发的。scrypt不仅计算所需时间长,而且占用的内存也多,使得并行计算多个摘要异常困难,因此利用彩虹表进行暴力攻击更加困难。scrypt没有在生产环境中大规模应用,并且缺乏仔细的审察和广泛的函数库支持。但是,scrypt在算法层面只要没有破绽,它的安全性应该高于PBKDF2。</p><p><br/></p><p>五、请使用bcrypt!请使用bcrypt!请使用bcrypt!</p><p><br/></p><p> bcrypt是跨平台的、专门为密码存储而设计的算法,bcrypt所接受的密码长度必须是8至56个字符,并将在内部被转化为448位的密钥。基于Blowfish加密算法变形而来。</p><p> bcrypt在默认情况下,在删除数据之前将使用随机数据三次覆盖原始输入文件,以阻挠可能会获得数据的人恢复数据的尝试。如果您不想使用此功能,可设定禁用此功能</p><p> bcrypt最大的好处是有一个参数,可用于调整计算强度,而且该参数是包括在输出的摘要中的。随着计算能力的提高,应该可以逐步增大这个参数,增大这个参数后并不会影响原来的用户。</p><p> bcrypt经过了很多安全专家的仔细分析,使用在以安全著称的OpenBSD中,一般认为它比PBKDF2更能承受随着计算能力加强而带来的风险。</p>', 1411544868, 2, 0, 0),
(69, 1, '李轩Lane', '初探哈希表HashTable', '哈希表是什么?对于高级语言内核来讲,哈希表hashTable绝对是数据结构中的关键,很多高级语言是显示的支持哈希表hashTable的,哈希表hashTable的实践应用也非常广泛,编译器会维护一个符号表来保存标记,提供增删改查等操作。', '哈希表是什么_哈希表概念_哈希冲突解决方案', '哈希表是什么?对于高级语言内核来讲,哈希表hashTable绝对是数据结构中的关键,很多高级语言是显示的支持哈希表hashTable的,哈希表hashTable的实践应用也非常广泛,编译器会维护一个符号表来保存标记,提供增删改查等操作。', '哈希表是什么,哈希表概念,哈希冲突解决方案', '哈希|hash', 1339, '<p>哈希表hashTable:哈希表hashTable是一种通过哈希函数,将特定的键映射到特定值的一种数据结构,它维护键和值之间一一对应关系。</p><p> 对于高级语言内核来讲,哈希表hashTable绝对是数据结构中的关键,很多高级语言是显示的支持哈希表hashTable的,哈希表hashTable的实践应用也非常广泛,编译器会维护一个符号表来保存标记,提供增删改查等操作。</p><p> 设计合理的哈希表hashTable的时间复杂度是O(1),而最最糟糕的哈希表hashTable时间复杂度为O(n),通常,它总是O(1)。而O(n)是因为,哈希表hashTable变成了一个链表。</p><p> 名词解释:</p><p> 1、键:操作数据的标识,例如PHP数组中的索引。</p><p> 2、槽:用于保存数据的坑。</p><p> 3、哈希映射函数:将键映射到数据应该存放的槽所在位置的函数。</p><p> 4、哈希冲突:哈希函数将两个不同的键映射到同一个索引的情况。</p><p> 哈希表hashTable可以理解为关联数组,数组使用键来寻址,如果键的范围较小且是数字的话, 我们可以直接使用数组来完成哈希表hashTable,而如果关键字范围太大,如果直接使用数组我们需要为所有可能的键申请空间。 很多情况下这是不现实的。即使空间足够,空间利用率也会很低,这并不理想。同时键也可能并不是数字, 在PHP中尤为如此,所以人们使用一种映射函数(哈希函数)来将键映射到特定的域中。</p><p> 通过合理设计的哈希函数,我们就能将键映射到合适的范围,因为我们的键空间可以很大(例如字符串键), 在映射到一个较小的空间中时可能会出现两个不同的键映射被到同一个index上的情况, 这就是我们所说的出现了冲突。 目前解决哈希表hashTable冲突的方法主要有两种:链接法和开放寻址法。目前PHP中哈希冲突解决方法就是链接法。</p><p> 冲突解决 - 连接法:</p><p> 链接法通过使用一个链表来保存槽值的方式来解决冲突,也就是当不同的键映射到一个槽中的时候使用链表来保存这些值。 所以使用链接法是在最坏的情况下,也就是所有的键都映射到同一个槽中了,这样哈希表hashTable就退化成了一个链表, 这样的话操作链表的时间复杂度则成了O(n),这样哈希表hashTable的性能优势就没有了, 所以选择一个合适的哈希函数是最为关键的。由于目前大部分的编程语言的哈希表hashTable实现都是开源的,大部分语言的哈希算法都是公开的算法, 虽然目前的哈希算法都能良好的将键进行比较均匀的分布,而这个假使的前提是键是随机的,正是由于算法的确定性, 这就导致了别有用心的黑客能利用已知算法的可确定性来构造一些特殊的键,让这些键都映射到 同一个槽位导致哈希表hashTable退化成单链表,导致程序的性能急剧下降,从而造成一些应用的吞吐能力急剧下降, 尤其是对于高并发的应用影响很大,通过大量类似的请求可以让服务器遭受DoS(服务拒绝攻击), 这个问题一直就存在着,只是最近才被各个语言重视起来。哈希冲突攻击利用的哈希表hashTable最根本的弱点是:开源算法和哈希实现的确定性以及可预测性, 这样攻击者才可以利用特殊构造的键来进行攻击。要解决这个问题的方法则是让攻击者无法轻易构造 能够进行攻击的键序列。PHP采用的是一种治标不治本的做法: 限制用户提交数据字段数量。这样可以避免大部分的攻击,不过应用程序通常会有很多的数据输入方式,比如,SOAP,REST等等, 比如很多应用都会接受用户传入的JSON字符串,在执行json_decode()的时候也可能会遭受攻击。 所以最根本的解决方法是让哈希表hashTable的碰撞键序列无法轻易的构造,目前PHP中还没有引入不增加额外的复杂性情况下的完美解决方案。</p><p> 冲突解决 - 开放寻址法:</p><p> 通常还有另外一种解决冲突的方法:开放寻址法。使用开放寻址法是槽本身直接存放数据, 在插入数据时如果键所映射到的索引已经有数据了,这说明发生了冲突,这是会寻找下一个槽, 如果该槽也被占用了则继续寻找下一个槽,直到寻找到没有被占用的槽,在查找时也使用同样的策略来进行。由于开放寻址法处理冲突的时候占用的是其他槽位的空间,这可能会导致后续的键在插入的时候更加容易出现 哈希冲突,所以采用开放寻址法的哈希表hashTable的装载因子不能太高,否则容易出现性能下降。</p><p><br/></p><p><br/></p><p>相关内容:<a href="http://www.lanecn.com/article/main/aid-54" target="_blank" title="用PHP的实现一个高效的数据库(文件存储,NOSQL)">用PHP的实现一个高效的数据库(文件存储,NOSQL)</a> 使用哈希表来构建自己的数据库 </p>', 1412939824, 1, 1, 0),
(70, 8, '李轩Lane', '算法一:桶排序', '什么是桶排序?桶排序是最快最简单的一种排序算法,甚至严格来说都不能算作算法。桶排序的优点是特别快,缺点是内存利用率特别差。桶排序的时间复杂度是O(M+N)。', '桶排序_什么是桶排序_桶排序的优缺点', '什么是桶排序?桶排序是最快最简单的一种排序算法,甚至严格来说都不能算作算法。桶排序的优点是特别快,缺点是内存利用率特别差。', '桶排序,什么是桶排序,桶排序的优缺点', '算法|桶排序', 1261, '<p style="white-space: normal;">桶排序, 是最快最简单的排序。有多宽维度,就要申请多大的数组。比如100分的试卷,就要申请101个数组(试卷是0-100分),有人考了100分,就把array[100]加1,有人考了90,就把array[90]加1,有人考了70,就把array[70]+1,又有人考了90,就把array[90]加1,那么从高到底打印,如果是某个分数是0个人,就不打印,如果某个分数是1个人,就打印一次,如果某个分数是2个人,就打印两次,上面的例子就是100,90,90,90,70。</p><p style="white-space: normal;">桶排序快是毋庸置疑的,但是,浪费了空间。比如打游戏,新手村里的小鸡攻击力是1,而最后一个副本的大BOSS的攻击力是999999999,那么就要对这个游戏的所有玩家和怪物的攻击力排序,就要申请一个1000000000长度的数组,就是array[1000000000]。假如每个字段的值都是1,1是整形,需要是4-8个字节(到底是4还是8取决于机器),那么这个数组就是1000000000/8/1024/1024=119MB。嗯哼?一个数组就要119M,你就是13年淘宝双11的160G内存的机器也拖不起吧。</p><p style="white-space: normal;">桶排序的时间复杂度是O(M+N),是最快的排序,它也是最简单的排序。</p><p style="white-space: normal;"><br/></p><p style="white-space: normal;">C语言代码:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">#include <stdio.h></p><p style="white-space: normal;">int main(){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> int i, j, num, score, rank[100];</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> for(i=0; i<101; i++){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> rank[i] = 0;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> }</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> //需要录入多少个数据</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> printf("How many?\\n");</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> scanf("%d", &num);</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> for(i=0; i<num; i++){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> printf("Enter Score:\\n");</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> scanf("%d", &score);</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> //我们的试卷是0-100分的哦</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> if(score<0 || score > 100){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> printf("Error");</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> return 1;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> }</p><p style="white-space: normal;"> //进桶<br/></p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> rank[score]++;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> score = 0;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> }</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> printf("Rank:\\n");</p><p style="white-space: normal;"> //打印</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> for(i=0; i<101; i++){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> for(j=0; j<rank[i]; j++){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> printf("%d,", i);</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> }</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> }</p><p style="white-space: normal;">}</p><p style="white-space: normal;">[/code]</p>', 1414420088, 1, 2, 0),
(71, 8, '李轩Lane', '算法二:冒泡排序', '冒泡排序是最被人熟知的排序算法。什么是冒泡排序?冒泡排序的特点是好邻居好说话,核心思想是一个每次比较两个相邻的元素,如果他们的大小顺序反了,就把他们交换过来。N个数就有N-1轮,每一轮把一个数字放在它正确的位置上。冒泡排序时间复杂度是O(N2)', '冒泡排序_什么是冒泡排序_冒泡排序的优缺点', '什么是冒泡排序?冒泡排序的特点是好邻居好说话,核心思想是一个每次比较两个相邻的元素,如果他们的大小顺序反了,就把他们交换过来。', '冒泡排序,什么是冒泡排序,冒泡排序的优缺点', '算法|冒泡排序', 663, '<p>冒泡排序是最被人熟知的排序算法。什么是冒泡排序?冒泡排序的特点是好邻居好说话,核心思想是一个每次比较两个相邻的元素,如果他们的大小顺序反了,就把他们交换过来。N个数就有N-1轮,每一轮把一个数字放在它正确的位置上。冒泡排序时间复杂度是O(N2)。C语言代码如下:</p><p>[code]</p><p>#include <stdio.h></p><p><br/></p><p>int main(){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> int i, j, num, score, tmp;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("How many?\\n");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> scanf("%d", &num);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> int rank[num];</p><p><span class="Apple-tab-span" style="white-space:pre"></span> for(i=0; i<num; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("Enter Score:\\n");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> scanf("%d", &score);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //我们的试卷是0-100分的哦</p><p><span class="Apple-tab-span" style="white-space:pre"></span> if(score<0 || score > 100){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("Error");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> return 1;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> rank[i] = score;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("Rank:\\n");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //冒泡排序</p><p><span class="Apple-tab-span" style="white-space:pre"></span> for(i=0; i<num-1; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> for(j=0; j<num-i; j++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> if(rank[j] > rank[j+1]){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> tmp = rank[j];</p><p><span class="Apple-tab-span" style="white-space:pre"></span> rank[j] = rank[j+1];</p><p><span class="Apple-tab-span" style="white-space:pre"></span> rank[j+1] = tmp;<span class="Apple-tab-span" style="white-space:pre"></span></p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> for(i=0; i<num; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("%d,", rank[i]);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> return 0;</p><p>}</p><p>[/code]</p>', 1414502666, 0, 0, 0),
(72, 8, '李轩Lane', '算法三:快速排序', '什么是快速排序?快速排序,是最常用的排序算法,就是选择待排序的列表中的其中一个数字,作为基准数,然后把小于基准数的所有数字放到这个数的左边,大于这个数的所有数字放到基准数的右边。然后将左右分成2部分,继续上述操作,不断递归。快速排序的时间复杂度是O(NlogN)。', '快速排序_什么是快速排序_快速排序的优缺点', '什么是快速排序?快速排序,是最常用的排序算法,就是选择待排序的列表中的其中一个数字,作为基准数,然后把小于基准数的所有数字放到这个数的左边,大于这个数的所有数字放到基准数的右边。然后将左右分成2部分,继续上述操作,不断递归。', '快速排序,什么是快速排序,快速排序的优缺点', '算法|快速排序', 1697, '<p>快速排序,是最常用的排序算法,就是选择待排序的列表中的其中一个数字,作为基准数,然后把小于基准数的所有数字放到这个数的左边,大于这个数的所有数字放到基准数的右边。这个时候开始分为两部分,左边和右边。第一部分,在左边中选择一个数字作为基准数,把小于这个数字的放到这个数的左边,大于这个数的放到这个数的右边。第二部分,在右边中选择一个数字作为基准数,把小于这个数字的放到这个数的左边,大于这个数的放到这个数的右边。ok,很明显,递归!</p><p>快速排序的时间复杂度在最坏的情况下是O(N2),因为和冒泡排序一样,需要两两交换。平均情况下,快速排序的时间复杂度是O(NlogN)。</p><p>下面是C语言的代码</p><p>[code]</p><p>#include <stdio.h></p><p>void quickSort(int left, int right, int *rank);</p><p>int main(){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> int i, j, num, score, tmp;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("How many?\\n");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> scanf("%d", &num);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> int rank[num];</p><p><span class="Apple-tab-span" style="white-space:pre"></span> for(i=0; i<num; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("Enter Score:\\n");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> scanf("%d", &score);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //我们的试卷是0-100分的哦</p><p><span class="Apple-tab-span" style="white-space:pre"></span> if(score<0 || score > 100){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("Error");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> return 1;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> rank[i] = score;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("Rank:\\n");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> quickSort(0, num-1, rank);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> for(i=0; i<num; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("%d,", rank[i]);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> return 0;</p><p>}</p><p>void quickSort(int left, int right, int *rank){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> if(left > right){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> return;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> int i, j, t, tmp;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> i = left;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> j = right;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> t = rank[left];</p><p><span class="Apple-tab-span" style="white-space:pre"></span> while(i!=j){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> while(rank[j]>=t && i<j)</p><p><span class="Apple-tab-span" style="white-space:pre"></span> j--;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> while(rank[i]<=t && i<j)</p><p><span class="Apple-tab-span" style="white-space:pre"></span> i++;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> if(i < j){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> tmp = rank[j];</p><p><span class="Apple-tab-span" style="white-space:pre"></span> rank[j] = rank[i];</p><p><span class="Apple-tab-span" style="white-space:pre"></span> rank[i] = tmp;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> rank[left] = rank[i];</p><p><span class="Apple-tab-span" style="white-space:pre"></span> rank[i] = t;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> quickSort(left, i-1, rank);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> quickSort(i+1, right, rank);</p><p>}</p><p>[/code]</p><p><br/></p>', 1414593784, 1, 0, 0),
(73, 8, '李轩Lane', '算法四:队列', '什么是队列?队列就是先进先出,也就是现实生活中我们的排队,先来的先走,后来的后走,一个接一个,这就是队列。队列优点很明显,按照顺序谁也不抢。缺点也很明显,如果排头的走了,那么第二个要排头,第三个要到第二个,每个都要前进一步,对计算机来讲这就是资源的损耗。', '队列_什么是队列_队列的优缺点', '什么是队列?队列就是先进先出,也就是现实生活中我们的排队,先来的先走,后来的后走,一个接一个,这就是队列。队列优点很明显,按照顺序谁也不抢。缺点也很明显,如果排头的走了,那么第二个要排头,第三个要到第二个,每个都要前进一步,对计算机来讲这就是资源的损耗。', '队列,什么是队列,队列的优缺点', '', 652, '<p> 什么是队列?队列就是先进先出,也就是现实生活中我们的排队,先来的先走,后来的后走,一个接一个,这就是队列。队列优点很明显,按照顺序谁也不抢。缺点也很明显,如果排头的走了,那么第二个要排头,第三个要到第二个,每个都要前进一步,对计算机来讲这就是资源的损耗。队列也称为FIFO,就是First In, First Out.</p><p> 下面是队列的基本模型。我们有一个现成的队伍,数组ranks。然后出一个,head是指向队头的指针就加1,新来一个,就在队尾的后一个位置tail指向的位置存放,然后tail也加1。这样可以避免每出队一次,就要全部前移一次。</p><p>[code]</p><p>#include <stdio.h></p><p>void queue(int head, int tail, int *ranks);</p><p>int main(){</p><p> int ranks[100] = {4,5,0,6,8,1,3,9,7,2};</p><p> int head = 0;</p><p> int tail = 10;</p><p> queue(head, tail, ranks);</p><p>}</p><p>void queue(int head, int tail, int *ranks){</p><p> char more = '\\0';</p><p> int new = 0;</p><p> while(head < tail){</p><p> printf("----------------%d-------------------\\n", ranks[head]);</p><p> head++;</p><p> printf("Add Element?[y/n] \\n");</p><p> scanf(" %c", &more);</p><p> if(more == 'y'){</p><p> printf("New Number:\\n");</p><p> scanf(" %d", &new);</p><p> ranks[tail] = new;</p><p> tail++;</p><p> }</p><p> }</p><p>}</p><p>[/code]</p>', 1415111662, 1, 0, 0),
(74, 8, '李轩Lane', '算法五:栈', '什么是栈?先进后出/后进先出为栈。与队列相反。就是先来的要排到最后,后来的却可以先走。栈最早是由图灵奖命名来源者图灵发明,最初为解决程序的调用和返回。而栈的应用之一就是递归。', '栈_什么是栈_栈的优缺点', '什么是栈?先进后出/后进先出为栈。与队列相反。就是先来的要排到最后,后来的却可以先走。', '栈,什么是栈,栈的优缺点', '', 542, '<p> 什么是栈?先进后出/后进先出为栈。与队列相反。就是先来的要排到最后,后来的却可以先走。栈最早是由图灵奖命名来源者图灵发明,最初为解决程序的调用和返回。而栈的应用之一就是递归。</p><p>[code]</p><p>#include <stdio.h></p><p>void stack(int top, int *ranks);</p><p>int main(){</p><p> int ranks[100] = {4,5,0,6,8,1,3,9,7,2};</p><p> //栈顶<span class="Apple-tab-span" style="white-space:pre"></span></p><p> int top = 9;</p><p> stack(top, ranks);</p><p>}</p><p>void stack(int top, int *ranks){</p><p> char more = '\\0';</p><p> int new = 0;</p><p> while(top >= 0){</p><p> printf("----------------%d-------------------\\n", ranks[top]);</p><p> top--;</p><p> printf("Add Element?[y/n] \\n");</p><p> scanf(" %c", &more);</p><p> if(more == 'y'){</p><p> printf("New Number:\\n");</p><p> scanf(" %d", &new);</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>ranks[++top] = new;</p><p> }</p><p> }</p><p>}</p><p>[/code]</p>', 1415112140, 0, 0, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(75, 8, '李轩Lane', '算法六:深度优先搜索', '什么是深度优先搜索?理解深度优先搜索的关键在于解决“当前如何做”,至于“下一步如何做”和“当前如何做是一样的做法”。深度优先搜索是Depth First Search, DFS。', '深度优先搜索_什么是深度优先搜索_深度优先搜索的优缺点', '什么是深度优先搜索?理解深度优先搜索的关键在于解决“当前如何做”,至于“下一步如何做”和“当前如何做是一样的做法”。', '深度优先搜索,什么是深度优先搜索,深度优先搜索的优缺点', '算法|深度优先搜索', 1489, '<p> 什么是深度优先搜索?理解深度优先搜索的关键在于解决“当前如何做”,至于“下一步如何做”和“当前如何做是一样的做法”。深度优先搜索是Depth First Search, DFS。基本模型如下:</p><p>[code]</p><p>void dfs(int step){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> 边界判断</p><p><span class="Apple-tab-span" style="white-space:pre"></span> 循环遍历去尝试每一种可能for(int i=0; i<n; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> 继续下一步dfs(step + 1);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p>}</p><p>[/code]</p><p> 下面看一个实例。我们有三个数字,1,2,3.求这三个数字的所有可能的搭配。那么,答案就是123,132,213,231,312,321共6种。我给出的答案顺序,也是深度优先搜索的顺序。</p><p> 假设我们有1,2,3的三张牌,地上有编号为一、二、三的三个纸箱。我们每次都把手中最小的牌放到箱子里,然后走到下一个箱子。</p><p> 第一步:在一号箱子面前,放入最小的手牌1,然后前进一步。此时手牌是2和3。判断边界(手牌是否为空)。</p><p> 第二步:在二号箱子面前,放入最小的手牌2,然后前进一步。此时手牌是3。判断边界(手牌是否为空)。</p><p> 第三步:在三号箱子面前,放入最小的手牌3,然后前进一步,此时手牌是空。判断边界(手牌是否为空)。</p><p> 第四步:手牌为空,触发边界条件。遍历打印箱子中的数字,结果是123。</p><p> 第五步:取出三号箱子的牌,后退一步。此时手牌是3。</p><p> 第六步:取出二号箱子的牌,此时手牌是2和3,2已经放过了,所以放3,然后前进一步。此时手牌是2。判断边界(手牌是否为空)。</p><p> 第七步:在三号箱子面前,放入最小手牌2,然后前进一步,此时手牌是空。判断边界(手牌是否为空)。</p><p> 第八步:手牌为空,触发边界条件。遍历打印箱子中的数字,结果是132。</p><p> 第九步:取出三号箱子的牌,后退一步。此时手牌是2。</p><p> 第十步:取出二号箱子的牌,此时手牌是2和3,2和3都已经放过了,所以再退一部。此时手牌是2。判断边界(手牌是否为空)。</p><p> 第十一步:取出一号箱子的牌,此时手牌是1,2和3,1以及放过了,所以放剩下的最小手牌,就是2,然后前进一步,此时手牌是1,3。判断边界(手牌是否为空)。</p><p> 第十二步:在二号箱子面前,放入最小的手牌1,然后前进一步。此时手牌是3。判断边界(手牌是否为空)。</p><p> 第十三步:在三号箱子面前,放入最小的手牌3,然后前进一步,此时手牌是空。判断边界(手牌是否为空)。</p><p> 第十四步:手牌为空,触发边界条件。遍历打印箱子中的数字,结果是213。</p><p> ………………………………………………………………………………</p><p> 恩,就是这样子,直接上代码了。C版本的深度优先遍历的模型。</p><p> Ps:本实例和代码均来自《啊哈!算法》一书。不过上面的过程是我自己手打原创哦^_^</p><p>[code]</p><p>#include <stdio.h></p><p>void dfs(int step);</p><p>int a[10], book[10], n;</p><p>int main(){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //1-9的整数</p><p><span class="Apple-tab-span" style="white-space:pre"></span> scanf("%d", &n);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //从第一个箱子开始。直接从下标1开始。0不要了。</p><p><span class="Apple-tab-span" style="white-space:pre"></span> dfs(1);</p><p>}</p><p>void dfs(int step){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> int i;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //已经到最后一个箱子了。</p><p><span class="Apple-tab-span" style="white-space:pre"></span> if(step == n+1){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> for(i=1; i<=n; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("%d", a[i]);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> printf("\\n");</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //在第step个箱子面前,按照1,2,3...n的顺序一一尝试</p><p><span class="Apple-tab-span" style="white-space:pre"></span> for(i=1; i<=n; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //等于0就是i还没有使用,还在手里。</p><p><span class="Apple-tab-span" style="white-space:pre"></span> if(book[i] == 0){</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //使用i</p><p><span class="Apple-tab-span" style="white-space:pre"></span> a[step] = i;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //标记i已经使用</p><p><span class="Apple-tab-span" style="white-space:pre"></span> book[i] = 1;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //前进一步</p><p><span class="Apple-tab-span" style="white-space:pre"></span> dfs(step + 1);</p><p><span class="Apple-tab-span" style="white-space:pre"></span> //回收step箱子里的牌</p><p><span class="Apple-tab-span" style="white-space:pre"></span> book[i] = 0;</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p><span class="Apple-tab-span" style="white-space:pre"></span> }</p><p>}</p><p>[/code]</p>', 1415546394, 0, 0, 0),
(76, 8, '李轩Lane', '算法七:广度优先搜索', '什么是广度优先搜索?广度优先搜索也称为宽度优先搜索,一层一层不断的扩展来达到搜索的目的。以一个点为中心,将上下左右4个点都搜索过后,再以这4个点分别为中心点,搜索该中心点的上下左右4个点,依次类推。', '广度优先搜索_什么是广度优先搜索_广度优先搜索的优缺点', '什么是广度优先搜索?广度优先搜索也称为宽度优先搜索,一层一层不断的扩展来达到搜索的目的。以一个点为中心,将上下左右4个点都搜索过后,再以这4个点分别为中心点,搜索该中心点的上下左右4个点,依次类推。', '广度优先搜索,什么是广度优先搜索,广度优先搜索的优缺点', '算法|广度优先排序', 1706, '<p> 什么是广度优先搜索?广度优先搜索也称为宽度优先搜索,一层一层不断的扩展来达到搜索的目的。以一个点为中心,将上下左右4个点都搜索过后,再以这4个点分别为中心点,搜索该中心点的上下左右4个点,依次类推。</p><p> 场景:比如我们要从点(1,1)到点(5,5),我们使用广度优先算法来找出最短路劲。</p><p> 第一步:将点(1,1)的上下左右4个点找出,走过的点忽略,不能走的点忽略(比如水,墙),那么只有(1,2)和(2,1)2个点,因为(1,0)和(0,1)已经超出了地图范围。</p><p> 第二步:将第一步得出的点(1,2)的上下左右4个点找出,走过的点忽略,不能走的点忽略,那么只有(1,3)和(2,2)2个点,因为(0,2)已经超出了地图范围,(1,1)已经走过了。</p><p> 第三步:将第一步得出的将点(2,1)的上下左右4个点找出,走过的点忽略,不能走的点忽略,那么只有(3,1)这一个点,因为(2,0)已经超出了地图范围,(1,1)已经走过了。(2,2)在第二部已经得出了。</p><p> ……</p><p> 依次类推,将每个点都尝试过后,则比较每种方式的长度,得出最短路径。</p><p><br/></p><p> 代码将展示另一个实例。我们都玩过炸弹人的游戏,地图中有墙和敌人,在地图的空地上方一个炸弹,炸弹会在上下左右4个直线方向炸出4道火花,敌人会被炸死,而墙不会受到影响。基于此,我们用广度优先算法来实现哪个点可以炸死的敌人数量最多。</p><p><br/></p><p> 1、建立一个队列,将起点(人物刚出的时候在地图的位置)作为队列的第一个元素。按照队列的顺序来执行。</p><p> 2、将这个点的上下左右4个点也依次入队。然后把起点出队。</p><p> 3、这时把队列的第一个元素(起点的右边的点)作为中心点,将这个点的上下左右4个点依次入队,然后这个点出队。 4、这时把队列的第一个元素(起点的下边的点)作为中心点,将这个点的上下左右4个点依次入队,然后这个点出队。 5、这时把队列的第一个元素(起点的左边的点)作为中心点,将这个点的上下左右4个点依次入队,然后这个点出队。 6、这时把队列的第一个元素(起点的上边的点)作为中心点,将这个点的上下左右4个点依次入队,然后这个点出队。 7、这时把队列的第一个元素(起点的右边的点的右边的点)作为中心点,将这个点的上下左右4个点依次入队,然后这个点出队。</p><p> ……</p><p> 依次类推,总之,就是把一个点作为中心点,然后把这个点的上下左右依次入队,按照队列顺序,队列中每个点都要作为中心点,把中心点上下左右4个点依次入队。直到队列没有元素时停止。</p><p> 我们约定,敌人为“G”,墙为“#”,空地为“.”。</p><p> 计算一个点可以炸死的敌人数量的函数getnum()如下:</p><p>[code]</p><p>int getnum(int i, int j){</p><p> int x, y, sum=0;</p><p> //统计点(i, j)的左边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> x--;</p><p> }</p><p> //统计点(i, j)的右边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> x++;</p><p> }</p><p> //统计点(i, j)的上边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> y--;</p><p> }</p><p> //统计点(i, j)的下边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> y++;</p><p> }</p><p> return sum;</p><p>}</p><p>[/code]</p><p><br/></p><p> 入队操作的代码如下:</p><p>[code]</p><p>//起点入队</p><p> que[tail].x = startx;</p><p> que[tail].y = starty;</p><p> tail++;</p><p> book[startx][starty] = 1;</p><p> max = getnum(startx, starty);</p><p> mx = startx;</p><p> my = starty;</p><p>[/code]</p><p><br/></p><p style="white-space: normal;"> 广度优先搜索的核心代码如下:</p><p style="white-space: normal;">[code]</p><p>//广度优先搜索的核心部分</p><p> while(head < tail){</p><p> for(k = 0; k< 4; k++){</p><p> tx = que[head].x + next[k][0];</p><p> ty = que[head].y + next[k][1];</p><p> //越界</p><p> if(tx<0 || tx>n-1 || ty<0 || ty>n-1) continue;</p><p> //不是平地?以前走过了?</p><p> if(a[tx][ty]!='.' || book[tx][ty]!=0) continue;</p><p> //这个点可以走,并且没走过,就标记为走过了,然后入队</p><p> book[tx][ty] = 1;</p><p> que[tail].x = tx;</p><p> que[tail].y = ty;</p><p> tail++;</p><p> sum = getnum(tx, ty);</p><p> if(sum > max){</p><p> max = sum;</p><p> mx = tx;</p><p> my = ty;</p><p> }</p><p> }</p><p> //这个点的上下左右都看过了,就出队</p><p> head++;</p><p> }</p><p style="white-space: normal;">[/code]</p><p style="white-space: normal;"><br/></p><p style="white-space: normal;"> 完整的代码如下:</p><p style="white-space: normal;">[code]</p><p>#include<stdio.h></p><p>struct Note{</p><p> int x;</p><p> int y;</p><p>};</p><p>char a[20][20];</p><p>//获取点(i, j)可以炸死多少敌人。</p><p>int getnum(int i, int j);</p><p>void main(){</p><p> struct Note que[401];</p><p> int head=1, tail=1;</p><p> int book[20][20] = {0};</p><p> int i, k, tx, ty, startx, starty, sum, max=0, mx, my, m, n;</p><p> int next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};</p><p> //地图长n,宽m,起始位置坐标为startx,starty</p><p> scanf("%d %d %d %d", &n, &m, &startx, &starty);</p><p> for(i=0; i<n; i++){</p><p> printf("Line : %d", i);</p><p> scanf("%s", a[i]);</p><p> }</p><p> //起点入队</p><p> que[tail].x = startx;</p><p> que[tail].y = starty;</p><p> tail++;</p><p> book[startx][starty] = 1;</p><p> max = getnum(startx, starty);</p><p> mx = startx;</p><p> my = starty;</p><p> //广度优先搜索的核心部分</p><p> while(head < tail){</p><p> for(k = 0; k< 4; k++){</p><p> tx = que[head].x + next[k][0];</p><p> ty = que[head].y + next[k][1];</p><p> //越界</p><p> if(tx<0 || tx>n-1 || ty<0 || ty>n-1) continue;</p><p> //不是平地?以前走过了?</p><p> if(a[tx][ty]!='.' || book[tx][ty]!=0) continue;</p><p> //这个点可以走,并且没走过,就标记为走过了,然后入队</p><p> book[tx][ty] = 1;</p><p> que[tail].x = tx;</p><p> que[tail].y = ty;</p><p> tail++;</p><p> sum = getnum(tx, ty);</p><p> if(sum > max){</p><p> max = sum;</p><p> mx = tx;</p><p> my = ty;</p><p> }</p><p> }</p><p> //这个点的上下左右都看过了,就出队</p><p> head++;</p><p> }</p><p> printf("炸弹放在%d,%d的位置,可以炸死%d人", mx, my, max);</p><p>}</p><p><br/></p><p>int getnum(int i, int j){</p><p> int x, y, sum=0;</p><p> //统计点(i, j)的左边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> x--;</p><p> }</p><p> //统计点(i, j)的右边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> x++;</p><p> }</p><p> //统计点(i, j)的上边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> y--;</p><p> }</p><p> //统计点(i, j)的下边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> y++;</p><p> }</p><p> return sum;</p><p>}</p><p style="white-space: normal;">[/code]</p><p><br/></p><p>输入:第一次要求输入地图的长、宽、起始X坐标、起始Y坐标:13 13 3 3</p><p>第二次要求输入的就是地图了,每次一行,如下</p><p><br/></p><p>#############</p><p>#GG.GGG#GGG.#</p><p>###.#G#G#G#G#</p><p>#.......#..G#</p><p>#G#.###.#G#G#</p><p>#GG.GGG.#.GG#</p><p>#G#.#G#.#.#.#</p><p>##G...G.....#</p><p>#G#.#G###.#G#</p><p>#...G#GGG.GG#</p><p>#G#.#G#G#.#G#</p><p>#GG.GGG#G.GG#</p><p>#############</p><p><br/></p><p>输出:炸弹放在7,11的位置,可以炸死10人.</p><p><br/></p><p>Ps:代码案例来源于《啊哈!算法》一书</p><p><br/></p><p>炸弹人游戏本篇用广度优先搜索来解决,也可以深度优先搜索来实现。关于《<a href="http://www.lanecn.com/article/main/aid-77" target="_self">深度优先搜索实现炸弹人游戏</a>》点击查看</p>', 1415714198, 3, 0, 0),
(77, 8, '李轩Lane', '算法八:炸弹人游戏之深度优先搜索', '深度优先搜索,我们本篇讲用深度优先搜索来解决上一篇的炸弹人游戏,领略深度优先搜索和广度优先搜索的不同。', '算法八:炸弹人游戏之深度优先搜索', '深度优先搜索,我们本篇讲用深度优先搜索来解决上一篇的炸弹人游戏,领略深度优先搜索和广度优先搜索的不同。深度优先搜索,我们本篇讲用深度优先搜索来解决上一篇的炸弹人游戏,领略深度优先搜索和广度优先搜索的不同。', '炸弹人游戏,深度优先搜索', '算法|深度优先搜索', 1041, '<p> 上一章我们编写了一个小游戏:炸弹人游戏。使用广度优先搜索解决的,本章将用深度优先搜索来解决。</p><p>《<a href="http://www.lanecn.com/article/main/aid-76" target="_self" title="广度优先搜索">关于广度优先搜索解决炸弹人游戏和炸弹人游戏的描述</a>》点击查看</p><p>《<a href="http://www.lanecn.com/article/main/aid-75" target="_blank" title="深度优先搜索">什么是深度优先搜索</a>》点击查看 </p><p><br/></p><p>深度优先搜索的核心代码如下:说白了,就是一条路走到黑,然后回头倒着走看看有没有别的岔路口,不断递归。<br/></p><p>[code]</p><p>void dfs(int x, int y){</p><p> int next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};</p><p> int k, sum, tx, ty;</p><p> sum = getnum(x, y);</p><p> if(sum > max){</p><p> max = sum;</p><p> mx = x;</p><p> my = y;</p><p> }</p><p> for(k=0; k<4; k++){</p><p> tx = x + next[k][0];</p><p> ty = y + next[k][1];</p><p> //判断边界</p><p> if(tx<0 || tx > n-1 || ty < 0 || ty > n-1) continue;</p><p> //判断墙和是否走过</p><p> if(a[tx][ty] != '.' || book[tx][ty] != 0) continue;</p><p> book[tx][ty] = 1;</p><p> dfs(tx, ty);</p><p> }</p><p> return;</p><p>}</p><p>[/code]</p><p><br/></p><p>深度优先搜索实现炸弹人游戏的完整代码如下:</p><p>[code]</p><p>#include<stdio.h></p><p>char a[20][20];</p><p>int book[20][20], max, mx, my, n, m;</p><p>//获取点(i, j)可以炸死多少敌人。</p><p>int getnum(int i, int j);</p><p>//深度优先搜索,对点(x, y)进行深度优先搜索</p><p>void dfs(int x, int y);</p><p>void main(){</p><p> int i, startx, starty;</p><p> //地图长n,宽m,起始位置坐标为startx,starty</p><p> scanf("%d %d %d %d", &n, &m, &startx, &starty);</p><p> for(i=0; i<n; i++){</p><p> printf("Line : %d", i);</p><p> scanf("%s", a[i]);</p><p> }</p><p> book[startx][starty] = 1;</p><p> max = getnum(startx, starty);</p><p> mx = startx;</p><p> my = starty;</p><p> dfs(startx, starty);</p><p> printf("炸弹放在%d,%d的位置,可以炸死%d人", mx, my, max);</p><p>}</p><p><br/></p><p>int getnum(int i, int j){</p><p> int x, y, sum=0;</p><p> //统计点(i, j)的左边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> x--;</p><p> }</p><p> //统计点(i, j)的右边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> x++;</p><p> }</p><p> //统计点(i, j)的上边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> y--;</p><p> }</p><p> //统计点(i, j)的下边可以消灭多少敌人</p><p> x = i;</p><p> y = j;</p><p> //如果是墙就停止</p><p> while(a[x][y] != '#'){</p><p> if(a[x][y] == 'G') sum++;</p><p> y++;</p><p> }</p><p> return sum;</p><p>}</p><p>void dfs(int x, int y){</p><p> int next[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};</p><p> int k, sum, tx, ty;</p><p> sum = getnum(x, y);</p><p> if(sum > max){</p><p> max = sum;</p><p> mx = x;</p><p> my = y;</p><p> }</p><p> for(k=0; k<4; k++){</p><p> tx = x + next[k][0];</p><p> ty = y + next[k][1];</p><p> //判断边界</p><p> if(tx<0 || tx > n-1 || ty < 0 || ty > n-1) continue;</p><p> //判断墙和是否走过</p><p> if(a[tx][ty] != '.' || book[tx][ty] != 0) continue;</p><p> book[tx][ty] = 1;</p><p> dfs(tx, ty);</p><p> }</p><p> return;</p><p>}</p><p>[/code]</p><p><br/></p><p>输入:第一次要求输入地图的长、宽、起始X坐标、起始Y坐标:13 13 3 3</p><p><br/></p><p>第二次要求输入的就是地图了,每次一行,如下:</p><p>#############</p><p>#GG.GGG#GGG.#</p><p>###.#G#G#G#G#</p><p>#.......#..G#</p><p>#G#.###.#G#G#</p><p>#GG.GGG.#.GG#</p><p>#G#.#G#.#.#.#</p><p>##G...G.....#</p><p>#G#.#G###.#G#</p><p>#...G#GGG.GG#</p><p>#G#.#G#G#.#G#</p><p>#GG.GGG#G.GG#</p><p>#############</p><p><br/></p><p>输出:炸弹放在7,11的位置,可以炸死10人.</p><p><br/></p><p>Ps:代码案例来源于《啊哈!算法》一书</p>', 1415886004, 0, 0, 0),
(78, 8, '李轩Lane', '算法九:深度优先搜索和广度优先搜索来遍历图', '图的遍历,我们用深度优先搜索遍历图和广度优先搜索遍历图。最简单的一种图的遍历-穷举!', '图的遍历_深度优先搜索遍历图_广度优先搜索遍历图', '图的遍历,我们用深度优先搜索遍历图和广度优先搜索遍历图。最简单的一种图的遍历-穷举!', '图的遍历,深度优先搜索遍历图,广度优先搜索遍历图', '算法|图的遍历', 24205, '<p>图的遍历,我们用深度优先搜索遍历图和广度优先搜索遍历图。最简单的一种图的遍历-穷举!</p><p>如下的图:</p><p><img src="http://lanecn-upload.stor.sinaapp.com/image/20141114_1415961007_142469.png" title="20141114_1415961007_142469.png" alt="F1062065-133F-4EB8-AD78-3E7168FF00C7.png"/></p><p>点1和点2,3,5有边,点3和点5有边,点2和点4有边。mac画的好累。。没有鼠标。。</p><p><br/></p><p>输入:第一行是点的总数和边的总数,下面几行都是那几个点有边。</p><p> 5 5</p><p> 1 2</p><p> 1 3</p><p> 1 5</p><p> 2 4</p><p> 3 5</p><p><br/></p><p>深度优先搜索来遍历图,代码如下:<br/></p><p>[code]</p><p class="p1">#include <span class="s1"><stdio.h></span></p><p class="p2"><span class="s2">int</span> a[<span class="s3">101</span>][<span class="s3">101</span>];</p><p class="p2"><span class="s2">int</span> book[<span class="s3">101</span>];</p><p class="p2"><span class="s2">int</span> m, n, sum=<span class="s3">0</span>;</p><p class="p2"><span class="s2">void</span> dfs(<span class="s2">int</span> node);</p><p class="p2"><span class="s2">int</span> main(){</p><p class="p2"> <span class="s2">int</span> i, j, x, y;</p><p class="p2"> scanf(<span class="s1">"%d %d"</span>, &m, &n);</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=n; i++){</p><p class="p2"> <span class="s2">for</span>(j=<span class="s3">1</span>; j<=n; j++){</p><p class="p2"> a[i][j] = <span class="s3">0</span>;</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">0</span>; i<m; i++){</p><p class="p2"> scanf(<span class="s1">"%d %d"</span>, &x, &y);</p><p class="p2"> a[x][y] = <span class="s3">1</span>;</p><p class="p2"> a[y][x] = <span class="s3">1</span>;</p><p class="p2"> }</p><p class="p2"> book[<span class="s3">1</span>] = <span class="s3">1</span>;</p><p class="p2"> dfs(<span class="s3">1</span>);</p><p class="p2">}</p><p class="p2"><span class="s2">void</span> dfs(<span class="s2">int</span> node){</p><p class="p2"> printf(<span class="s1">"%d "</span>, node);</p><p class="p2"> sum++;</p><p class="p2"> <span class="s2">if</span>(sum == n){</p><p class="p2"> <span class="s2">return</span>;</p><p class="p2"> }</p><p class="p2"> <span class="s2">int</span> i = <span class="s3">0</span>;</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=n; i++){</p><p class="p2"> <span class="s2">if</span>(book[i] != <span class="s3">0</span> || a[node][i] != <span class="s3">1</span>) <span class="s2">continue</span>;</p><p class="p2"> book[i] = <span class="s3">1</span>;</p><p class="p2"> dfs(i);</p><p class="p2"> }</p><p class="p2">}</p><p>[/code]</p><p>输出:1,2,4,3,5</p><p><br/></p><p><br/></p><p>广度优先搜索来遍历图,代码如下:</p><p>输入:第一行是点的总数和边的总数,下面几行都是那几个点有边。</p><p><br/></p><p>[code]</p><p class="p1">#include <span class="s1"><stdio.h></span></p><p class="p2"><span class="s2">int</span> a[<span class="s3">101</span>][<span class="s3">101</span>];</p><p class="p2"><span class="s2">int</span> book[<span class="s3">101</span>];</p><p class="p2"><span class="s2">int</span> m, n;</p><p class="p2"><span class="s2">void</span> dfs(<span class="s2">int</span> node);</p><p class="p3"><br/></p><p class="p2"><span class="s2">int</span> main(){</p><p class="p2"> <span class="s2">int</span> i, j, x, y;</p><p class="p2"> scanf(<span class="s1">"%d %d"</span>, &m, &n);</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=n; i++){</p><p class="p2"> <span class="s2">for</span>(j=<span class="s3">1</span>; j<=n; j++){</p><p class="p2"> a[i][j] = <span class="s3">0</span>;</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">0</span>; i<m; i++){</p><p class="p2"> scanf(<span class="s1">"%d %d"</span>, &x, &y);</p><p class="p2"> a[x][y] = <span class="s3">1</span>;</p><p class="p2"> a[y][x] = <span class="s3">1</span>;</p><p class="p2"> }</p><p class="p2"> book[<span class="s3">1</span>] = <span class="s3">1</span>;</p><p class="p4"><span class="s4"> </span>//<span class="s5">广度优先搜索</span></p><p class="p2"> <span class="s2">int</span> head = <span class="s3">0</span>, tail = <span class="s3">0</span>;</p><p class="p2"> <span class="s2">int</span> queue[<span class="s3">25</span>];</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">0</span>; i<<span class="s3">25</span>; i++) queue[i] = <span class="s3">0</span>;</p><p class="p2"> queue[tail] = <span class="s3">1</span>;</p><p class="p2"> tail++;</p><p class="p2"> <span class="s2">while</span>(head<tail){</p><p class="p2"> printf(<span class="s1">"%d "</span>, queue[head]);</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=n; i++){</p><p class="p2"> <span class="s2">if</span>(book[i] != <span class="s3">0</span> || a[queue[head]][i] != <span class="s3">1</span>) <span class="s2">continue</span>;</p><p class="p2"> book[i] = <span class="s3">1</span>;</p><p class="p2"> queue[tail] = i;</p><p class="p2"> tail++;</p><p class="p2"> }</p><p class="p2"> head++;</p><p class="p2"> }</p><p class="p2">}</p><p>[/code]</p><p>输出:1,2,3,5,4</p>', 1415960734, 2, 0, 0),
(79, 8, '李轩Lane', '算法十:多源最短路径(任意两点最短路径)之Floyd-Warshall算法', '任意两点最短路径被称为多源最短路径,即给定任意两个点,一个出发点,一个到达点,求这两个点的之间的最短路径,就是任意两点最短路径问题,多源最短路径,而Floyd-Warshall算法最简单,只有5行代码,即可解决这个问题。', '多源最短路径_任意两点最短路径_Floyd-Warshall算法', '任意两点最短路径被称为多源最短路径,即给定任意两个点,一个出发点,一个到达点,求这两个点的之间的最短路径,就是任意两点最短路径问题,多源最短路径,而Floyd-Warshall算法最简单,只有5行代码,即可解决这个问题。', '多源最短路径,任意两点最短路径,Floyd-Warshall算法', '算法|多源最短路径', 847, '<p> 任意两点最短路径被称为多源最短路径,即给定任意两个点,一个出发点,一个到达点,求这两个点的之间的最短路径,就是任意两点最短路径问题,多源最短路径,而Floyd-Warshall算法最简单,只有5行代码,即可解决这个问题。</p><p> 比如三个城市,城市1,城市2,城市3。从城市1到城市3需要10公里,从城市1到城市2需要3公里,从城市2到城市3需要4公里,如下图:</p><p><img src="http://lanecn-upload.stor.sinaapp.com/image/20141119_1416369106_721801.png" title="20141119_1416369106_721801.png" alt="4F966EED-1AD1-4FD9-ABB4-0448137A13E4.png"/></p><p><br/></p><p>那么从城市1到城市3,如何走更近?1到3是直达,但是却10公里,而1到2到3,虽然转一下,但是距离短了,只要7公里。</p><p>这就引出了我们的观念,一个点到另一个点要变短,就要引入第三个点,甚至第四个点,第五个点。</p><p><br/></p><p>假设在map数组里存储了点i到点j的距离map[i][j],那么我们引入第三个点k,则如果点i到点j的距离比点i到点k加点k到点j,则点i到点j的最短距离是点i到点k加点k到点j。</p><p>即如果map[i][j]<map[i][k]+map[k][j],则map[i][j]=map[i][k]+map[k][j]</p><p><br/></p><p>这就是Floyd-Warshall算法。核心代码如下:</p><p>[code]</p><p class="p1"><span class="s1">//</span>只能从k中转</p><p class="p2"> <span class="s2">for</span>(k=<span class="s3">1</span>; k<=cityCount; k++){</p><p class="p2"> <span class="s4">//</span><span class="s5">从i</span><span class="s5">出发</span></p><p class="p2"> <span class="s2">for(i</span>=<span class="s3">1</span>; i<=cityCount; i++){</p><p class="p2"> <span class="s4">//</span><span class="s5">到j</span><span class="s5">结束</span></p><p class="p2"> <span class="s2">for</span>(j=<span class="s3">1</span>; j<=cityCount; j++){</p><p class="p2"> <span class="s2">if</span>(<span class="s6">map</span>[i][j] > (<span class="s6">map</span>[i][k] + <span class="s6">map</span>[k][j])){</p><p class="p2"> <span class="s6">map</span>[i][j] = <span class="s6">map</span>[i][k]+ <span class="s6">map</span>[k][j];</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> }</p><p>[/code]</p><p>三个for循环,加一个if,加一个赋值,简简单单的五行代码,就实现了任意一个点到任意另一个点的最短距离。</p><p>显而易见,时间复杂度是O(n3).</p><p><br/></p><p>完整代码如下</p><p>[code]</p><p class="p1">#include <span class="s1"><stdio.h></span></p><p class="p2"><span class="s2">int</span> map[<span class="s3">11</span>][<span class="s3">11</span>] = {{<span class="s3">0</span>}};</p><p class="p2"><span class="s2">int</span> book[<span class="s3">11</span>] = {<span class="s3">0</span>};</p><p class="p2"><span class="s2">int</span> main(){</p><p class="p2"> <span class="s2">int</span> i, j, k, cityCount, roadCount, length, x, y;</p><p class="p2"> <span class="s4">scanf</span>(<span class="s1">"%d %d"</span>, &cityCount, &roadCount);</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=cityCount; i++){</p><p class="p2"> <span class="s2">for</span>(j=<span class="s3">1</span>; j<=cityCount; j++){</p><p class="p2"> <span class="s5">map</span>[i][j] = <span class="s3">0</span>;</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=roadCount; i++){</p><p class="p2"> <span class="s4">scanf</span>(<span class="s1">"%d %d %d"</span>, &x, &y, &length);</p><p class="p2"> <span class="s5">map</span>[x][y] = length;</p><p class="p2"> }</p><p class="p3"><span class="s6"> </span>//<span class="s7">只能从</span>k<span class="s7">中转</span></p><p class="p2"> <span class="s2">for</span>(k=<span class="s3">1</span>; k<=cityCount; k++){</p><p class="p2"> <span class="s8">//</span><span class="s9">从</span><span class="s8">i</span><span class="s9">出发</span></p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=cityCount; i++){</p><p class="p2"> <span class="s8">//</span><span class="s9">到</span><span class="s8">j</span><span class="s9">结束</span></p><p class="p2"> <span class="s2">for</span>(j=<span class="s3">1</span>; j<=cityCount; j++){</p><p class="p2"> <span class="s2">if</span>(<span class="s5">map</span>[i][j] == <span class="s3">0</span> || <span class="s5">map</span>[i][k] == <span class="s3">0</span> || <span class="s5">map</span>[k][j] == <span class="s3">0</span>) <span class="s2">continue</span>;</p><p class="p2"> <span class="s2">if</span>(<span class="s5">map</span>[i][j] > (<span class="s5">map</span>[i][k] + <span class="s5">map</span>[k][j])){</p><p class="p2"> <span class="s5">map</span>[i][j] = <span class="s5">map</span>[i][k]+ <span class="s5">map</span>[k][j];</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> }</p><p class="p3"><span class="s6"> </span>//<span class="s7">现在的</span>map<span class="s7">里就是从一个点</span>i<span class="s7">到另一个点</span>j<span class="s7">的最短距离</span>map[i][j]</p><p class="p2"> <span class="s4">printf</span>(<span class="s1">"点1到点5的最短路径是:%d"</span>, <span class="s5">map</span>[<span class="s3">1</span>][<span class="s3">5</span>]);</p><p class="p2">}</p><p>[/code]</p><p><br/></p><p>输入</p><p class="p1">5 8</p><p class="p1">1 2 3</p><p class="p1">1 3 2</p><p class="p1">1 5 10</p><p class="p1">2 3 1</p><p class="p1">3 5 3</p><p class="p1">3 4 5</p><p class="p1">4 5 1</p><p class="p1">2 4 8</p><p><br/></p><p>输入,5</p>', 1416367906, 1, 1, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(80, 8, '李轩Lane', '算法十一:Dijkstra算法 - 一个点到各个点的最短路径', '什么是Dijkstra算法?Dijkstra算法是指定一个源点,求得这个源点到各个点的最短路径。Dijkstra算法通过不断的松弛边,每次更新相邻点的路径,使之两点之间的距离成为最短的路径。Dijkstra算法缺点是不能有负权边的值。', 'Dijkstra算法_什么是Dijkstra算法_Dijkstra算法的优缺点', '什么是Dijkstra算法?Dijkstra算法是指定一个源点,求得这个源点到各个点的最短路径。Dijkstra算法通过不断的松弛边,每次更新相邻点的路径,使之两点之间的距离成为最短的路径。Dijkstra算法缺点是不能有负权边的值。', 'Dijkstra算法,什么是Dijkstra算法,Dijkstra算法的优缺点', '算法|Dijkstra算法', 1905, '<p> Dijkstra算法是指定一个源点,求得这个源点到各个点的最短路径。Dijkstra算法通过不断的松弛边,每次更新相邻点的路径,使之两点之间的距离成为最短的路径。Dijkstra算法缺点是不能有负权边的值。</p><p> 松弛边:点A到点B的距离是10,点A到点C的距离是15,点B到点C的距离是3,那么点A到点C的最短距离就是13。此时15这个值将会被废弃,永不使用,以后谈论点A到点C的距离都是直接说13。这就是松弛边。<br/></p><p> 现在有如下图,6个点,9条边,边是有方向的哦。</p><p><br/></p><p style="text-align: center;"><img src="http://lanecn-upload.stor.sinaapp.com/image/20141223_1419347010_272506.jpg" title="20141223_1419347010_272506.jpg" alt="无标题.jpg"/></p><p><br/></p><p> Dijkstra算法的步骤,假设源点为点(1):</p><p> 1、先求得源点到各个点的距离。则数组为dis['1'=>0, '2'=>1, '3'=>12, '4'=>'∞', '5'=>'∞', '6'=>'∞'];并且用数组e[i][j]表示点i到点j的距离。</p><p> 2、将各个点分为2个部分,P部分是已知的点1到该点距离为最短路径的点的集合,此时P部分只有点1到点1的距离为0是已知的,点1到点2,点1到点3的距离是不是最短路径暂时不可是,所以他们不属于这部分。Q部分是未知的点1到该点距离为最短路径的点的集合。</p><p> 3、在集合Q中选择一个点,这个点距离源点(1)号点最近,即步骤一得出的dis数组中该key所对应的值最小,此时这个点为2号点,因为dis[2]最小,为1。则点1到点2的距离dis[2]是最短的路径,已经已知了,所以将(2)号点加入到集合P中。此时以(2)好点为源点,对所有的边松弛一次,看看有没有一个点X,可以使得点1到点2再到点X的距离小于点1到点X的距离,如果有,则点1到点X的最短路径就是点1到点2再到点X的值。即如果dis[3] > dis[2] + e[2][x],则dis[3] = dis[2] + e[2][x]。</p><p> 4、重复第三步,知道集合Q为空。</p><p><br/></p><p>Dijkstra算法的代码如下:</p><p>[code]</p><p class="p1">#include <span class="s1"><stdio.h></span></p><p class="p2"><span class="s2">int</span> main(){</p><p class="p2"> <span class="s2">int</span> startPoint = <span class="s3">1</span>;</p><p class="p2"> <span class="s2">int</span> limitValue = <span class="s3">999</span>;</p><p class="p2"> <span class="s2">int</span> e[<span class="s3">10</span>][<span class="s3">10</span>], dis[<span class="s3">10</span>], book[<span class="s3">10</span>], i, j, m, n, point1, point2, length, u, v, min;</p><p class="p3"><span class="s4"> </span>//n<span class="s5">是点数,</span>m<span class="s5">是边数</span></p><p class="p2"> <span class="s6">scanf</span>(<span class="s1">"%d %d"</span>, &n, &m);</p><p class="p4"><span class="s7"> </span><span class="s8">//</span>如果<span class="s8">i=j</span>,就是自己到自己的距离,那么就是<span class="s8">0</span>,否则,就初始化非正无穷</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=n; i++){</p><p class="p2"> <span class="s2">for</span>(j=<span class="s3">1</span>; j<=n; j++){</p><p class="p2"> <span class="s2">if</span>(i==j)</p><p class="p2"> e[i][j] = <span class="s3">0</span>;</p><p class="p2"> <span class="s2">else</span></p><p class="p2"> e[i][j] = limitValue;</p><p class="p2"> }</p><p class="p2"> }</p><p class="p4"><span class="s7"> </span><span class="s8">//</span>边的长度,即点到点的距离</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=m; i++){</p><p class="p2"> <span class="s6">scanf</span>(<span class="s1">"%d %d %d"</span>, &point1, &point2, &length);</p><p class="p2"> e[point1][point2] = length;</p><p class="p2"> }</p><p class="p4"><span class="s7"> </span><span class="s8">//</span>初始化源点到各点的距离的数组<span class="s8">, </span>我们的源点为<span class="s8">1</span></p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=n; i++){</p><p class="p2"> dis[i] = e[startPoint][i];</p><p class="p2"> }</p><p class="p4"><span class="s7"> </span><span class="s8">//book</span>数组用来标记那个点是已经走过了的。</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=n; i++){</p><p class="p2"> book[i] = <span class="s3">0</span>;</p><p class="p2"> }</p><p class="p3"><span class="s4"> </span>//<span class="s5">从源点开始</span></p><p class="p2"> book[startPoint] = <span class="s3">1</span>;</p><p class="p4"><span class="s7"> </span><span class="s8">//</span>以下就是<span class="s8">Dijkstra</span>算法的重点,是核心思想</p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">1</span>; i<=n-<span class="s3">1</span>; i++){</p><p class="p3"><span class="s4"> </span>//<span class="s5">找到离远点最近的点</span></p><p class="p2"> min = limitValue;</p><p class="p2"> <span class="s2">for</span>(j=<span class="s3">1</span>; j<=n; j++){</p><p class="p2"> <span class="s2">if</span>(book[j] == <span class="s3">0</span> && dis[j] < min){</p><p class="p2"> min = dis[j];</p><p class="p2"> u = j;</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> book[u] = <span class="s3">1</span>;</p><p class="p2"> <span class="s2">for</span>(v=<span class="s3">1</span>; v<=n; v++){</p><p class="p3"><span class="s4"> </span>//<span class="s5">如果小于</span>limitValue<span class="s5">,则证明点</span>u<span class="s5">到点</span>v<span class="s5">是有路走的</span></p><p class="p2"> <span class="s2">if</span>(e[u][v] < limitValue){</p><p class="p2"> <span class="s2">if</span>(dis[v] > dis[u] + e[u][v]){</p><p class="p2"> dis[v] = dis[u] + e[u][v];</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2"> <span class="s9">//</span><span class="s10">输出</span></p><p class="p2"> <span class="s2">for</span>(i=<span class="s3">0</span>; i<=n; i++){</p><p class="p2"> <span class="s6">printf</span>(<span class="s1">"%d "</span>, dis[i]);</p><p class="p2"> }</p><p class="p2"> <span class="s2">return</span> <span class="s3">0</span>;</p><p class="p2">}</p><p>[/code]</p><p><br/></p><p>输入:</p><p>[code]</p><p>6 9</p><p>1 2 1</p><p>1 3 12</p><p>2 3 9</p><p>2 4 3</p><p>3 5 5</p><p>4 3 4</p><p>4 5 13</p><p>4 6 15</p><p>5 6 4</p><p>[/code]</p><p>输出</p><p>[code]</p><p>0 1 8 4 13 17</p><p>[/code]</p><p>这个输出便是点1到各个点的最短距离。</p><p><br/></p><p>Ps:本算法参考《啊哈!算法》一书。</p>', 1419346168, 2, 0, 0),
(81, 12, '李轩Lane', '初探Swift一 - 变量与数据类型', '初探Swift,Swift变量声明,let和var分别声明常量和变量。Swift数据类型,包括整型,浮点型,字符型,布尔型,可选型等,已经强制类型转换和if选择', 'Swift变量声明_Swift数据类型_Swift条件选择_初探Swift', '初探Swift,Swift变量声明,let和var分别声明常量和变量。Swift数据类型,包括整型,浮点型,字符型,布尔型,可选型等,已经强制类型转换和if选择', 'Swift变量声明,Swift数据类型,Swift条件选择', 'swift|ios', 737, '<p> 初探Swift,Swift变量声明,let和var分别声明常量和变量。Swift数据类型,包括整型,浮点型,字符型,布尔型,可选型等,已经强制类型转换和if选择。</p><p>[code]</p><p class="p1">//-----------<span class="s1">变量相关</span>----------</p><p class="p1">//<span class="s1">常量</span></p><p class="p2"><span class="s2">let</span> maxNum = <span class="s3">1000</span></p><p class="p1">//<span class="s1">变量</span></p><p class="p2"><span class="s2">var</span> index = <span class="s3">0</span></p><p class="p3"><br/></p><p class="p2"><span class="s2">var</span> a=<span class="s3">0.0</span>, y=<span class="s3">0</span>, z=<span class="s3">0</span></p><p class="p2">a = <span class="s3">1</span>;</p><p class="p2">a = <span class="s3">1.1</span>;</p><p class="p1">//<span class="s1">报错</span></p><p class="p1">//a = "a";</p><p class="p3"><br/></p><p class="p4"><span class="s4">//</span>显示申明类型</p><p class="p2"><span class="s2">var</span> test : <span class="s5">Int</span></p><p class="p2">test = <span class="s3">10</span></p><p class="p3"><br/></p><p class="p2"><span class="s2">var</span> red, green, blue : <span class="s5">Int</span></p><p class="p4"><span class="s4">//</span>十进制</p><p class="p2">red = <span class="s3">17</span>;</p><p class="p4"><span class="s4">//</span>二进制,以<span class="s4">0b</span>开头</p><p class="p5"><span class="s6">red = </span>0b10001</p><p class="p4"><span class="s4">//</span>八进制,以<span class="s4">0o</span>开头</p><p class="p2">red = <span class="s3">0o21</span></p><p class="p4"><span class="s4">//</span>十六进制,以<span class="s4">0x</span>开头</p><p class="p2">red = <span class="s3">0x11</span></p><p class="p3"><br/></p><p class="p4"><span class="s4">//</span>科学计数法</p><p class="p2"><span class="s2">let</span> b = <span class="s3">0.012</span></p><p class="p5"><span class="s2">let</span><span class="s6"> c = </span>1.2e-2</p><p class="p3"><br/></p><p class="p4"><span class="s4">//</span>多位整数的表示法</p><p class="p5"><span class="s2">let</span><span class="s6"> e = </span>1000000</p><p class="p5"><span class="s2">let</span><span class="s6"> f = </span>1_000_000</p><p class="p5"><span class="s2">let</span><span class="s6"> g = </span>1_000_000</p><p class="p3"><br/></p><p class="p4"><span class="s4">//</span>自动类型转换</p><p class="p2"><span class="s2">let</span> h:<span class="s5">Float</span> = <span class="s3">1</span></p><p class="p1">//Xcode beta2<span class="s1">会将</span>1.2<span class="s1">转为</span>1<span class="s1">,而</span>Xcode beta3<span class="s1">直接报错。</span></p><p class="p1">//let i:Int = 1.2</p><p class="p3"><br/></p><p class="p4"><span class="s4">//</span>强制类型转换</p><p class="p2"><span class="s2">let</span> j:<span class="s5">Double</span> = Double(h) + b</p><p class="p3"><br/></p><p class="p1">//<span class="s1">变量名,任何</span>unicode<span class="s1">都可以</span></p><p class="p2"><span class="s2">let</span> <span class="s1">姓名</span> = <span class="s7">"</span><span class="s8">小明</span><span class="s7">"</span></p><p class="p2">println(<span class="s1">姓名</span> + <span class="s7">"</span><span class="s8">,</span><span class="s7"> </span><span class="s8">你好</span><span class="s7">"</span>)</p><p class="p3"><br/></p><p class="p3"><br/></p><p class="p3"><br/></p><p class="p1">//----------<span class="s1">选择</span>-----------</p><p class="p2"><span class="s2">let</span> bool1 = true</p><p class="p2"><span class="s2">let</span> bool2:<span class="s5">Bool</span> = false</p><p class="p3"><br/></p><p class="p4"><span class="s4">//</span>即使语句块只有一行,花括号<span class="s4">{}</span>也不能省略<span class="s4">,</span>在选择里,只有<span class="s4">true</span>和<span class="s4">false</span>,<span class="s4">1</span>和<span class="s4">0</span>都是不可以的。</p><p class="p2"><span class="s2">if</span> bool1{</p><p class="p2"> println(<span class="s7">"hello"</span>);</p><p class="p2">}<span class="s2">else</span> <span class="s2">if</span> bool2{</p><p class="p2"> println(<span class="s7">"world"</span>);</p><p class="p6"><span class="s6">}</span>else<span class="s6">{</span></p><p class="p2"> println(<span class="s7">"swift"</span>);</p><p class="p2">}</p><p class="p3"><br/></p><p class="p3"><br/></p><p class="p3"><br/></p><p class="p1">//---------<span class="s1">元组</span>-----------</p><p class="p2"><span class="s2">let</span> tuples_1 = (<span class="s7">"</span><span class="s8">小明</span><span class="s7">"</span>, <span class="s7">"</span><span class="s8">男</span><span class="s7">"</span>, <span class="s3">21</span>, true)</p><p class="p4"><span class="s4">//</span>元组可以赋值给变量</p><p class="p2"><span class="s2">let</span> (name, sex, age, status) = tuples_1</p><p class="p2">println(<span class="s7">"</span><span class="s8">姓名:</span><span class="s7">"</span>+name+<span class="s7">"</span><span class="s8">,性别:</span><span class="s7">"</span>+sex);</p><p class="p4"><span class="s4">//</span>元祖可以用<span class="s4">.0</span>,<span class="s4">.1</span>,<span class="s4">.2</span>来访问</p><p class="p2">println(<span class="s7">"</span><span class="s8">姓名:</span><span class="s7">"</span>+tuples_1.<span class="s3">0</span>+<span class="s7">"</span><span class="s8">,性别:</span><span class="s7">"</span>+tuples_1.<span class="s3">1</span>);</p><p class="p3"><br/></p><p class="p4"><span class="s4">//</span>每个元祖都赋一个<span class="s4">key</span></p><p class="p2"><span class="s2">let</span> tuples_2 = (date:<span class="s7">"2015-01-05"</span>, time:<span class="s7">"16:06"</span>, author:<span class="s7">"lane"</span>)</p><p class="p2">println(<span class="s7">"</span><span class="s8">日期:</span><span class="s7">"</span>+tuples_2.date+<span class="s7">" "</span>+tuples_2.time+<span class="s7">"</span><span class="s8">,作者:</span><span class="s7">"</span>+tuples_2.author);</p><p class="p2">println(<span class="s7">"</span><span class="s8">日期:</span><span class="s7">"</span>+tuples_2.<span class="s3">0</span>+<span class="s7">" "</span>+tuples_2.<span class="s3">1</span>+<span class="s7">"</span><span class="s8">,作者:</span><span class="s7">"</span>+tuples_2.<span class="s3">2</span>);</p><p class="p3"><br/></p><p class="p4"><span class="s4">//</span>至提取元组的第一个值,不关心后面的值</p><p class="p2"><span class="s2">let</span> login = (true, <span class="s7">"</span><span class="s8">小明</span><span class="s7">"</span>)</p><p class="p2"><span class="s2">let</span> (staic, <span class="s2">_</span>) = login</p><p class="p2"><span class="s2">if</span> staic{</p><p class="p2"> println(<span class="s7">"hi"</span>);</p><p class="p6"><span class="s6">}</span>else<span class="s6">{</span></p><p class="p2"> println(<span class="s7">"hi hi"</span>);</p><p class="p2">}</p><p class="p4"><span class="s4">//</span>元组的类型显示声明</p><p class="p2"><span class="s2">let</span> login2:(<span class="s5">Bool</span>, <span class="s5">String</span>) = (true, <span class="s7">"</span><span class="s8">小明</span><span class="s7">"</span>)</p><p class="p3"><br/></p><p class="p3"><br/></p><p class="p3">//可选型optional</p><p class="p2"><span class="s2">var</span> optionalVar1:<span class="s5">Int</span>?</p><p class="p7">optionalVar1<span class="s6"> = </span><span class="s3">1</span></p><p class="p3"><br/></p><p class="p2"><span class="s2">let</span> userAge = <span class="s7">"18"</span></p><p class="p2"><span class="s2">var</span> age = <span class="s9">userAge</span>.<span class="s10">toInt</span>()</p><p class="p3"><br/></p><p class="p2"><span class="s2">if</span>(<span class="s9">age</span> != <span class="s2">nil</span>){</p><p class="p2"> <span class="s10">println</span>(<span class="s7">"You age is "</span> + <span class="s5">String</span>(<span class="s9">age</span>!) )</p><p class="p6"><span class="s6">}</span>else<span class="s6">{</span></p><p class="p8"><span class="s6"> </span><span class="s10">println</span><span class="s6">(</span>"Invalidate userInput"<span class="s6">)</span></p><p class="p2">}</p><p class="p3"><br/></p><p class="p1">//<span class="s1">解包</span></p><p class="p2"><span class="s2">if</span> <span class="s2">let</span> userInput = <span class="s9">userAge</span>.<span class="s10">toInt</span>(){</p><p class="p2"> <span class="s10">println</span>(<span class="s7">"you age is </span>\\(userInput)<span class="s7">"</span>);</p><p class="p2">}</p><p class="p2"><span class="s2">let</span> m:<span class="s5">String</span>? = <span class="s7">"hello"</span>;</p><p class="p2"><span class="s2">let</span> n:<span class="s5">String</span>! = <span class="s7">"hi"</span>;</p><p>[/code]</p>', 1420511819, 2, 0, 0),
(82, 12, '李轩Lane', '初探Swift二 - 字符串', '初探Swift,Swift字符串、字符串处理、字符串函数。Swift字符串本质是一个对象,同其他语言一样,可以使用拼接、长度、子字符串查找、子字符串替换、子字符串删除等操作', 'Swift字符串_Swift字符串处理_Swift字符串函数_初探Swift', '初探Swift,Swift字符串、字符串处理、字符串函数。Swift字符串本质是一个对象,同其他语言一样,可以使用拼接、长度、子字符串查找、子字符串替换、子字符串删除等操作', 'Swift字符串,Swift字符串处理,Swift字符串函数', 'swift|ios', 2228, '<p> 初探Swift之Swift字符串、字符串处理、字符串函数。Swift字符串本质是一个对象,同其他语言一样,可以使用拼接、长度、子字符串查找、子字符串替换、子字符串删除等操作。</p><p class="p1"><br/></p><p class="p1">[code]</p><p class="p2"><span class="s1">//</span>字符串长度,长度为5</p><p class="p1" style="white-space: normal;">var str_es = "swift"</p><p class="p1" style="white-space: normal;">countElements(str_es)</p><p class="p1"><span class="s1">//中文</span>字符串长度,长度为2,一个汉子为1</p><p class="p1">var str_ch = "<span class="s2">你好</span>"</p><p class="p1">countElements(str_ch)</p><p class="p3"><br/></p><p class="p2"><span class="s1">//</span>声明一个字符</p><p class="p1">let myChar:Character = "!"</p><p class="p3"><br/></p><p class="p1">//<span class="s2">比较,同其他语言,是按照字典的顺序。与长度无关</span></p><p class="p1">let a = "abcd";</p><p class="p1">let b = "abd";</p><p class="p1">a == b</p><p class="p1">a > b</p><p class="p1">a < b</p><p class="p3"><br/></p><p class="p2"><span class="s1">//</span>字符串的前缀和后缀</p><p class="p1">let chapterNames = [</p><p class="p1"> "<span class="s2">第一:</span>1111",</p><p class="p1"> "<span class="s2">第二:</span>1111",</p><p class="p1"> "<span class="s2">第二:</span>1111a",</p><p class="p1"> "<span class="s2">第二:</span>1111",</p><p class="p1"> "<span class="s2">第三:</span>1111",</p><p class="p1"> "<span class="s2">第三:</span>1111a",</p><p class="p1">]</p><p class="p2"><span class="s1">//</span>前缀为<span class="s1">“</span>第二<span class="s1">”</span>的个数统计</p><p class="p1">var count = 0</p><p class="p1">for name in chapterNames{</p><p class="p1"> if name.hasPrefix("<span class="s2">第二</span>"){</p><p class="p1"> count++</p><p class="p1"> }</p><p class="p1">}</p><p class="p1">count</p><p class="p2"><span class="s1">//</span>后缀为<span class="s1">“a”</span>的个数统计</p><p class="p1">count = 0</p><p class="p1">for name in chapterNames{</p><p class="p1"> if name.hasSuffix("a"){</p><p class="p1"> count++</p><p class="p1"> }</p><p class="p1">}</p><p class="p1">count</p><p class="p3"><br/></p><p class="p3"><br/></p><p class="p3"><br/></p><p class="p1">//<span class="s2">字符串高级操作,需要引入</span>Foundation</p><p class="p1">import Foundation</p><p class="p3"><br/></p><p class="p1">var str = "hello WOrld";</p><p class="p2"><span class="s1">//</span>首字母大写</p><p class="p1">str.capitalizedString</p><p class="p1">//<span class="s2">大写</span></p><p class="p1">str.uppercaseString</p><p class="p1">//<span class="s2">小写</span></p><p class="p1">str.lowercaseString</p><p class="p3"><br/></p><p class="p2"><span class="s1">//</span>去除两边的空格</p><p class="p1">var str2 = " hi ! "</p><p class="p1">str2.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())</p><p class="p3"><br/></p><p class="p2"><span class="s1">//</span>字符串按照空格分割为数组,同php的explode函数</p><p class="p1">var str3 = "hello world"</p><p class="p1">str3.componentsSeparatedByString(" ")</p><p class="p3"><br/></p><p class="p2"><span class="s1">//</span>数组连接为字符串,同php的implode函数</p><p class="p1">var str4 = "_"</p><p class="p1">str4.join(["1","2","3"])</p><p class="p3"><br/></p><p class="p3">//查找字符串</p><p class="p1">var str = "Welcome to play Swift! Step by Step learn Swift language from now!"</p><p class="p1">str.rangeOfString("Step")</p><p class="p1">//第二个参数是枚举,可以根据Xcode的提示看到,NSStringCompareOptions.CaseInsensitiveSearch是不区分大小写</p><p class="p1">str.rangeOfString("welcome", options: NSStringCompareOptions.CaseInsensitiveSearch)</p><p class="p3"><br/></p><p class="p3">//字符串的开头位置</p><p class="p1">str.startIndex</p><p class="p1">//字符串的结束为止</p><p class="p1">str.endIndex</p><p class="p3"><br/></p><p class="p3">//Range</p><p class="p1">let strRange = Range<String.Index>(start:str.startIndex, end:str.endIndex)</p><p class="p1">//在Range范围内查找</p><p class="p1">let startIndex = str.startIndex</p><p class="p1">let endIndex:String.Index = advance(str.startIndex, 10)</p><p class="p1">let searchRange = Range<String.Index>(start:startIndex, end:endIndex)</p><p class="p1">str.rangeOfString("Step", options: NSStringCompareOptions.CaseInsensitiveSearch, range: searchRange)</p><p class="p3">//截取开头的4个字符</p><p class="p1">var toIndex = advance(str.startIndex, 4)</p><p class="p1">str.substringToIndex(toIndex)</p><p class="p1">[/code]</p>', 1420608857, 2, 0, 0),
(83, 8, '李轩Lane', '算法十二:Bellman-Ford算法 - 一个点到其他所有点的最短路径(可负边)', '什么是Bellman-Ford算法?Bellman-Ford算法是一种堪称完美的解决一个点到其他各点的最短路径的算法。Bellman-Ford算法的核心代码只有4行,可以解决负权边的问题。', 'Bellman-Ford算法_什么是Bellman-Ford算法_Bellman-Ford算法的优缺点', '什么是Bellman-Ford算法?Bellman-Ford算法是一种堪称完美的解决一个点到其他各点的最短路径的算法。Bellman-Ford算法的核心代码只有4行,可以解决负权边的问题。', 'Bellman-Ford算法,什么是Bellman-Ford算法,Bellman-Ford算法的优缺点', '算法|Bellman-Ford算法', 701, '<p> 什么是Bellman-Ford算法?Bellman-Ford算法是一种堪称完美的解决一个点到其他各点的最短路径的算法。Bellman-Ford算法的核心代码只有4行,可以解决负权边的问题。</p><p> Bellman-Ford算法的核心代码只有四行,如下</p><p>[code]</p><p>for(k=1; k<=n-1; k++)</p><p> for(i=1; i<=m; i++)</p><p> if(dis[v[i]] > dis[u[i]] + w[i])</p><p> dis[v[i]] = dis[u[i]] + w[i];</p><p>[/code]</p><p> Bellman-Ford算法的核心代码意思是:遍历每一个点,其中每一次遍历时,遍历所有的边,对每个边进行一次松弛操作。</p><p> 松弛什么?请点击查看。<a href="http://www.lanecn.com/article/main/aid-80" target="_self">算法十一:Dijkstra算法 - 一个点到各个点的最短路径</a></p><p> 现在有如下图,共5个点5条边,边都是有向边,其中点1到点2的距离为负数。如下:</p><p style="text-align: center;"><img src="http://lanecn-upload.stor.sinaapp.com/image/20150108_1420725728_690049.jpg" title="20150108_1420725728_690049.jpg" alt="无标题.jpg"/></p><p><br/></p><p> 为什么会进行n-1次的循环呢?因为在不包含负权回路的路径中,n个点最多有n-1条边。在负权边中,负权回路会使最短路径不断的变小,永无止境。</p><p>完整代码如下:</p><p>[code]</p><p>#include <stdio.h></p><p>int main(){</p><p> int dis[10], i, k, m, n, u[10], v[10], w[10];</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>int maxValue = 9999;</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>//源点为1</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>int start = 1;</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>//读入点的个数n,边的个数m</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>scanf("%d %d", &n, &m);</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>//读入边</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>for(i=1; i<=m; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>scanf("%d %d %d", &u[i], &v[i], &w[i]);</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>}</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>//初始化dis数组。dis数组表示源点到各个点的初始距离,初始化时都为无穷大</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>for(i=0; i<=n; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>dis[i] = maxValue;</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>}</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>//源点到源点的距离为0</p><p><span class="Apple-tab-span" style="white-space:pre"></span> dis[start] = 0;</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>//Bellman-Ford算法的核心语句</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>for(k=1; k<=n-1; k++){</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>for(i=1; i<=m; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>if(dis[v[i]] > dis[u[i]] + w[i]){</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>dis[v[i]] = dis[u[i]] + w[i];</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>}</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>}</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>}</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>for(i=1; i<=n; i++){</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>printf("%d ", dis[i]);</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>}</p><p><span class="Apple-tab-span" style="white-space:pre"> </span>return 0;</p><p>}</p><p>[/code]</p><p>输入:</p><p>[code]</p><p>5 5</p><p>2 3 2</p><p>1 2 -3</p><p>1 5 5</p><p>4 5 2</p><p>3 4 3</p><p>[/code]</p><p>//输出:</p><p>[code]</p><p>0 -3 -1 2 4</p><p>[/code]</p><p> Bellman-Ford算法的时间复杂对为O(MN)。</p><p> 我们可以更加完善上面的代码。比如我们已经知道了使用Bellman-Ford算法不能包含负权回路,那么我们需要检测图中是否包含负权回路:</p><p>[code]</p><p>sign = 0</p><p>for(i=1; i<=m; i++)</p><p> if(dis[v[i]] > dis[u[i]] + w[i])</p><p> flag = 1;</p><p>if(sign == 1)</p><p> printf("此图有负权回路");</p><p>[/code]</p><p> 另外,在n-1轮循环中,若n-3轮已经松弛完毕,那么n-2和n-1两次循环便毫无意义,因此增加判断,若第x轮dis数组已经不再发生变化了,则不在继续进行循环。</p><p> 新版的完整代码如下:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">#include <stdio.h></p><p style="white-space: normal;">int main(){</p><p style="white-space: normal;"> int dis[10], i, k, m, n, u[10], v[10], w[10], sign, check;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>int maxValue = 9999;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>//源点为1</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>int start = 1;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>//读入点的个数n,边的个数m</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>scanf("%d %d", &n, &m);</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>//读入边</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>for(i=1; i<=m; i++){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>scanf("%d %d %d", &u[i], &v[i], &w[i]);</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>//初始化dis数组。dis数组表示源点到各个点的初始距离,初始化时都为无穷大</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>for(i=0; i<=n; i++){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>dis[i] = maxValue;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>//源点到源点的距离为0</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"></span> dis[start] = 0;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>//Bellman-Ford算法的核心语句</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>for(k=1; k<=n-1; k++){</p><p style="white-space: normal;"> //本轮dis数组是否发生变化</p><p style="white-space: normal;"> ckeck = 0;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>for(i=1; i<=m; i++){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if(dis[v[i]] > dis[u[i]] + w[i]){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>dis[v[i]] = dis[u[i]] + w[i];</p><p style="white-space: normal;"> check = 1;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</p><p style="white-space: normal;"> //本轮没有发生松弛,则不再继续进行了。</p><p style="white-space: normal;"> if(ckeck == 0){</p><p style="white-space: normal;"> break;</p><p style="white-space: normal;"> }</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</p><p style="white-space: normal;"> sign = 0</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>for(i=1; i<=m; i++)</p><p style="white-space: normal;"> <span class="Apple-tab-span" style="white-space: pre;"> </span> if(dis[v[i]] > dis[u[i]] + w[i])</p><p style="white-space: normal;"> <span class="Apple-tab-span" style="white-space: pre;"> </span> sign = 1;</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if(sign == 1){</p><p style="white-space: normal;"> <span class="Apple-tab-span" style="white-space: pre;"> </span> printf("此图有负权回路");</p><p style="white-space: normal;"> }else{</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span> for(i=1; i<=n; i++){</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-tab-span" style="white-space: pre;"> </span> </span> <span class="Apple-tab-span" style="white-space: pre;"> </span>printf("%d ", dis[i]);</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> <span class="Apple-tab-span" style="white-space: pre;"> </span> </span> }</p><p style="white-space: normal;"> }</p><p style="white-space: normal;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return 0;</p><p style="white-space: normal;">}</p><p style="white-space: normal;">[/code]</p><p style="white-space: normal;"><br/></p><p style="white-space: normal;"><br/></p><p style="white-space: normal;"> 在第每一轮循环中,其实只对上一次发生了松弛的边的相邻边判断是否需要松弛,因此,没有必要每次都进行m次循环(遍历所有的边判断是否需要松弛)。比如第一轮松弛了1->2和1->5,那么第二次仅仅判断2->3和5->4这两个边是否需要松弛,而不需要遍历所有的边。</p><p style="white-space: normal;"> 在这种情况下,我们引入队列,将需要松弛的点加入队列。每个点仅仅需要入队一次即可,因为入队多次是没有意义的。初始时我们将1号点(源点)加入队列。然后开始遍历队列,对队列中每个点的边进行松弛。队列为空时所得结果便是一个点到其他所有点的最短路径。这是对上述的Bellman-Ford算法一种极大的优化。当然,在最坏的情况下,和Bellman-Ford算法的时间复杂度是一样的,即O(MN)。此优化也可以解决负权边。</p><p style="white-space: normal;"> 完整代码如下:</p><p style="white-space: normal;">[code]</p><p>#include <stdio.h></p><p>int main(){</p><p> int dis[10] = {0}, i, j, k, m, n, u[10], v[10], w[10];</p><p> int book[10] = {0};</p><p> //first数组和next数组用来建立领接表,即first记录一个边的开头,比如第2号边的开头是点a,那么first[a]=2,a开头的下一个边是第5号边,则next[5]=a,组成一个链表的形式</p><p> int first[10], next[10];</p><p> int queue[100] = {0}, head = 1, tail = 1;</p><p> int maxValue = 9999;</p><p> //源点为1</p><p> int start = 1;</p><p> //读入点的个数n,边的个数m</p><p> scanf("%d %d", &n, &m);</p><p> //初始化dis数组。dis数组表示源点到各个点的初始距离,初始化时都为无穷大</p><p> //book用来标记那个点已经入队了,因为队列中同时出现同一个点多次是没有意义。</p><p> //first用来标记第i条边的起点</p><p> for(i=0; i<=n; i++){</p><p> dis[i] = maxValue;</p><p> book[i] = 0;</p><p> first[i] = -1;</p><p> }</p><p> //源点到源点的距离为0</p><p> dis[start] = 0;</p><p> //读入边</p><p> for(i=1; i<=m; i++){</p><p> scanf("%d %d %d", &u[i], &v[i], &w[i]);</p><p> //使用first和next两个数组建立领接表</p><p> next[i] = first[u[i]];</p><p> first[u[i]] = i;</p><p> }</p><p> //源点入队</p><p> queue[tail] = start;</p><p> tail++;</p><p> //标记已经入队的点</p><p> book[i] = start;</p><p> while(head < tail){</p><p> //当前需要处理的点,是队首的点,处理队首开头的所有边</p><p> k = first[queue[head]];</p><p> //扫描这个点开头的所有边</p><p> while( k != -1){</p><p> if(dis[v[k]] > dis[u[k]] + w[k]){</p><p> dis[v[k]] = dis[u[k]] + w[k];</p><p> if(book[v[k]] == 0){</p><p> queue[tail] = v[k];</p><p> tail++;</p><p> book[v[k]] = 1;</p><p> }</p><p> }</p><p> k = next[k];</p><p> }</p><p> //这个点开头的所有边都处理完了,就出队</p><p> book[queue[head]] = 0;</p><p> head++;</p><p> }</p><p> for(i=1; i<=n; i++){</p><p> printf("%d ", dis[i]);</p><p> }</p><p> getchar();getchar();</p><p> return 0;</p><p>}</p><p style="white-space: normal;">[/code]</p><p style="white-space: normal;">输入:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">5 5</p><p style="white-space: normal;">2 3 2</p><p style="white-space: normal;">1 2 -3</p><p style="white-space: normal;">1 5 5</p><p style="white-space: normal;">4 5 2</p><p style="white-space: normal;">3 4 3</p><p style="white-space: normal;">[/code]</p><p style="white-space: normal;">//输出:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">0 -3 -1 2 4</p><p style="white-space: normal;">[/code]</p><p>Ps:本算法参考《啊哈!算法》一书。</p>', 1420724998, 0, 0, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(84, 8, '李轩Lane', '算法十三:树的应用之堆 - 从小到大和从大到小排列的问题', '堆是一种特殊的完全二叉树,是树的一种常见的应用,最简单的便是解决将无序的数列从大到小排列或者从小到大排列。性能极佳。', '堆_什么是堆_堆的优缺点', '堆是一种特殊的完全二叉树,是树的一种常见的应用,最简单的便是解决将无序的数列从大到小排列或者从小到大排列。性能极佳。', '堆,什么是堆,堆的优缺点', '算法|堆', 860, '<p> 堆是一种特殊的完全二叉树,是树的一种常见的应用,最简单的便是解决将无序的数列从大到小排列或者从小到大排列。性能极佳。</p><p> 堆分两种,一种是最大堆,即所有的父节点都大于它的两个子节点。另一种是最小堆,即所有的父节点都小于它的两个子节点。</p><p> 如果一组数列,是无序的,要将它有序的排列,那么:</p><p>[code]</p><p>min = 9999;</p><p>for(i=0; i<n; i++){</p><p> if(num[i] < min){</p><p> min = num[i];<br/></p><p> }</p><p>}</p><p>[/code]</p><p> 这是最简单也是最高效的算法。可是,如果现在要删除最小数,在数列中插入一个任意一个数,此时再求最小值呢?那么就要重复上面的操作。那么问题来了,如果这个动作要重复100亿次呢?此时,时间复杂度就要100亿×数列的长度。即O(num.count*100亿)。这是不科学的,这个时候,我们引入了堆。</p><p> 最小堆的根节点永远是最小值!此时我们删掉了最小值,也就是根节点,在根节点的位置插入一个任意值,这个时候这个树就不是堆了,我们要把根节点向下移动,找到他合适的位置,也就是他要比他父节点大,比他的根节点小。此时这个树又恢复成了堆。</p><p> 现在有如下堆:</p><p style="text-align: center;"><img src="http://lanecn-upload.stor.sinaapp.com/image/20150108_1420731506_467049.jpg" title="20150108_1420731506_467049.jpg" alt="无标题.jpg"/></p><p><br/></p><p> 堆的黑色数组表示节点的编号,在圈圈里。红色数组表示这个节点的值。我们现在要从小到大排列一个数列。也就是对红色数组进行排序。</p><p> 1、1号节点是根节点,永远是最小值,这是最小堆的特性。反之最大堆的根节点是最大值。</p><p> 2、将1号节点的值改变为新插入的任意值。</p><p> 3、将新插入的值向下移动,使树恢复为最小堆。和它的左子节点还有右子节点比较,若大,则交换位置。如此反复,直到不能再交换位置了。<br/></p><p> 使用最小堆来从小到大排列一个无序数列的全部代码如下,时间复杂度为O(NLogN):</p><p>[code]</p><p>#include <stdio.h></p><p>//存放堆</p><p>int h[100];</p><p>//存储堆的元素个数</p><p>int n;</p><p>//交换元素</p><p>void swap(int x, int y){</p><p> int t;</p><p> t = h[x];</p><p> h[x] = h[y];</p><p> h[y] = t;</p><p>}</p><p>//向下调整</p><p>void siftdown(int i){</p><p> int t, flag=0;</p><p> //判断该点的做儿子是否存在</p><p> while(i*2 <= n && flag == 0){</p><p> //如果左儿子存在,则判断是否交换值</p><p> if(h[i] > h[i*2])</p><p> t = i * 2;</p><p> else</p><p> t = i;</p><p> //右儿子是否存在</p><p> if(i*2+1 <= n && h[t] > h[i*2+1]){</p><p> t = i*2+1;</p><p> }</p><p> //如果需要交换则交换</p><p> if( t!= i){</p><p> swap(t, i);</p><p> i = t;</p><p> //如果不需要交换,则准备退出while</p><p> }else{</p><p> flag = 1;</p><p> }</p><p> }</p><p>}</p><p>//向上调整,本函数本次将不会用到。</p><p>void siftup(int i){</p><p> int flag = 0;</p><p> while(i != 1 && flag == 0){</p><p> if(h[i] < h[i/2]){</p><p> swap(i, i/2);</p><p> i = i/2;</p><p> }else{</p><p> flag = 1;</p><p> }</p><p> }</p><p>}</p><p>//创建堆</p><p>void create(){</p><p> int i;</p><p> for(i=n/2; i>=1; i--){</p><p> siftdown(i);</p><p> }</p><p>}</p><p>//获取最小的元素</p><p>int getMin(){</p><p> int t;</p><p> //堆顶(根节点)为最小值</p><p> t = h[1];</p><p> //删除根节点,把堆的最后一个元素赋给根节点</p><p> h[1] = h[n];</p><p> n--;</p><p> //把根节点向下移动,使堆恢复最小堆</p><p> siftdown(1);</p><p> return t;</p><p>}</p><p>int main(){</p><p> int i, num;</p><p> scanf("%d", &num);</p><p> for(i=1; i<=num; i++){</p><p> scanf("%d", &h[i]);</p><p> }</p><p> n = num;</p><p> //创建堆</p><p> create();</p><p> //讲堆从小到大排列</p><p> for(i=1; i<=num; i++){</p><p> printf("%d ", getMin());</p><p> }</p><p> return 0;</p><p>}</p><p>[/code]<br/></p><p style="white-space: normal;">输入:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">14</p><p style="white-space: normal;">99 5 36 7 22 17 46 12 2 19 25 28 1 92</p><p style="white-space: normal;">[/code]</p><p style="white-space: normal;">//输出:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">1 2 5 7 12 17 19 22 25 28 36 46 92 99</p><p style="white-space: normal;">[/code]</p><p><br/></p><p> 若使用最大堆,根节点就是整个数列的最大值,那么将h[1]和h[n]交换位置,此时h[n]就是最大值,然后将h[1]向下移动已恢复新的堆。此时将堆的大小减一,重复上面的操作。时间复杂度将会从O(NLogN)降低到O(NLogK)</p><p> 完整代码如下:</p><p>[code]</p><p>#include <stdio.h></p><p>//存放堆</p><p>int h[100];</p><p>//存储堆的元素个数</p><p>int n;</p><p>//交换元素</p><p>void swap(int x, int y){</p><p> int t;</p><p> t = h[x];</p><p> h[x] = h[y];</p><p> h[y] = t;</p><p>}</p><p>//向下调整</p><p>void siftdown(int i){</p><p> int t, flag=0;</p><p> //判断该点的做儿子是否存在</p><p> while(i*2 <= n && flag == 0){</p><p> //如果左儿子存在,则判断是否交换值</p><p> if(h[i] < h[i*2])</p><p> t = i * 2;</p><p> else</p><p> t = i;</p><p> //右儿子是否存在</p><p> if(i*2+1 <= n && h[t] < h[i*2+1]){</p><p> t = i*2+1;</p><p> }</p><p> //如果需要交换则交换</p><p> if( t!= i){</p><p> swap(t, i);</p><p> i = t;</p><p> //如果不需要交换,则准备退出while</p><p> }else{</p><p> flag = 1;</p><p> }</p><p> }</p><p>}</p><p>//创建堆</p><p>void create(){</p><p> int i;</p><p> for(i=n/2; i>=1; i--){</p><p> siftdown(i);</p><p> }</p><p>}</p><p>//从小到大排序</p><p>int minToMaxSort(){</p><p> while(n>1){</p><p> swap(1, n);</p><p> n--;</p><p> siftdown(1);</p><p> }</p><p>}</p><p>int main(){</p><p> int i, num;</p><p> scanf("%d", &num);</p><p> for(i=1; i<=num; i++){</p><p> scanf("%d", &h[i]);</p><p> }</p><p> n = num;</p><p> //创建堆</p><p> create();</p><p> //排序</p><p> minToMaxSort();</p><p> for(i=1; i<=num; i++){</p><p> printf("%d ", h[i]);</p><p> }</p><p> return 0;</p><p>}</p><p>[/code]</p><p style="white-space: normal;">输入:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">14</p><p style="white-space: normal;">99 5 36 7 22 17 46 12 2 19 25 28 1 92</p><p style="white-space: normal;">[/code]</p><p style="white-space: normal;">//输出:</p><p style="white-space: normal;">[code]</p><p style="white-space: normal;">1 2 5 7 12 17 19 22 25 28 36 46 92 99</p><p style="white-space: normal;">[/code]</p><p style="white-space: normal;">Ps:本算法参考《啊哈!算法》一书。</p>', 1420730630, 0, 0, 0),
(85, 12, '李轩Lane', '初探Swift三 - 数组和字典', 'Swift的集合有两种,是数组和字典,显式声明和隐式声明、增删改查、基本操作等将在本文介绍。', 'Swift数组_Swift字典_Swift数组和字典基本操作_初探Swift', 'Swift的集合有两种,是Swift数组和Swift字典,显式声明和隐式声明、增删改查、Swift数组和字典基本操作等介绍。', 'Swift数组,Swift字典,Swift数组和字典基本操作', 'swift|ios', 772, '<p class="p1"> Swift的集合有两种,是数组和字典,显式声明和隐式声明、增删改查、基本操作等将在本文介绍。</p><p class="p1">/**<br/></p><p class="p3"><span class="s1"> * </span>数组的初始化</p><p class="p1"> */</p><p class="p3"><span class="s1">//</span>数组只能存同一种数据类型</p><p class="p1">var array = ["a", "b", "c"]</p><p class="p1">array[0] = "A"</p><p class="p3"><span class="s1">//显式</span>的声明存储字符串的数组</p><p class="p1">var array2:[String] = ["a", "b", "c"]</p><p class="p1">var array3:Array<String> = ["a", "b", "c"]</p><p class="p3"><span class="s1">//</span>创建空数组,存储<span class="s1">Int</span>类型</p><p class="p1">var array4 = [Int]()</p><p class="p1">var array5 = Array<String>()</p><p class="p1">var array6:[Int] = []</p><p class="p1">var array7:Array<Int> = []</p><p class="p3"><span class="s1">//</span>清空数组,清空后,后期仍然只能使用之前定义的数据类型</p><p class="p1">array2 = []</p><p class="p1">array2 = [String]()</p><p class="p1">array2 = Array<String>()</p><p class="p3"><span class="s1">//</span>初始化,<span class="s1">10</span>个值,每个值都为<span class="s1">0</span></p><p class="p1">var array8 = [Int](count:10, repeatedValue:0)</p><p class="p3"><span class="s1">//</span>数组合并</p><p class="p1">var array9 = [1, 2, 3]</p><p class="p1">var array10 = array8+array9</p><p class="p2"><br/></p><p class="p2"><br/></p><p class="p1">/**</p><p class="p3"><span class="s1"> * </span>数组的基本操作</p><p class="p1"> */</p><p class="p1">var array = ["A", "B", "C", "D"]</p><p class="p3"><span class="s1">//</span>数组的总数</p><p class="p1">array.count</p><p class="p3"><span class="s1">//</span>数组是否为空</p><p class="p1">array.isEmpty</p><p class="p3"><span class="s1">//</span>数组结尾加入新的元素</p><p class="p1">array.append("E")</p><p class="p1">array += ["F"]</p><p class="p1">array += ["G", "H"]</p><p class="p3"><span class="s1">//</span>数组任意位置加入新元素</p><p class="p1">array.insert("b", atIndex: 2)</p><p class="p3"><span class="s1">//</span>删除任意位置的元素,返回所删除的元素的值</p><p class="p1">array.removeAtIndex(1)</p><p class="p3"><span class="s1">//</span>删除最后一个元素</p><p class="p1">array.removeLast()</p><p class="p3"><span class="s1">//</span>删除所有元素</p><p class="p1">array.removeAll(keepCapacity: false)</p><p class="p3"><span class="s1">//</span>修改单个元素</p><p class="p1">array[0] = "AA"</p><p class="p3"><span class="s1">//</span>修改一组元素,若key为2...4,包含2,3,4三个key,而value只有一个,那么2,3,4三个值将只剩一个</p><p class="p1">array[2...4] = ["AA", "BB", "CC"]</p><p class="p1">array[2..<4] = ["AA", "bb"]</p><p class="p3"><span class="s1">//</span>遍历数组</p><p class="p1">for index in 0..<array.count{</p><p class="p1"> println("\\(index) -> \\(array[index])")</p><p class="p1">}</p><p class="p1">for item in array{</p><p class="p1"> println(item)</p><p class="p1">}</p><p class="p1">for (index, item) in enumerate(array){</p><p class="p1"> println("\\(index) -> \\(item)")</p><p class="p1">}</p><p class="p2"><br/></p><p class="p2"><br/></p><p class="p1">/**</p><p class="p1"> * <span class="s2">字典</span></p><p class="p3"><span class="s1"> * </span>键值对,可以是任意类型,但是一旦声明了一个字典,那么只能有一种搭配的类型。比如下面是键是<span class="s1">Int</span>,值是<span class="s1">String</span>。那么所有的键都必须是<span class="s1">Int</span>,所有的值都必须是<span class="s1">String</span></p><p class="p1"> */</p><p class="p3"><span class="s1">//</span>隐式声明字典</p><p class="p1">var dictionary = [1:"a", 2:"b", 3:"c"]</p><p class="p3"><span class="s1">//</span>现式声明字典</p><p class="p1">var dictionary1:Dictionary<Int, String> = [1:"a", 2:"b", 3:"c"]</p><p class="p1">var dictionary2:[Int:String] = [1:"a", 2:"b", 3:"c"]</p><p class="p3"><span class="s1">//</span>声明空字典、清空一个已有的字典</p><p class="p1">var dictionary3 = Dictionary<Int, String>()</p><p class="p1">var dictionary4 = [Int, String]()</p><p class="p3"><span class="s1">//</span>清空字典的特殊表示</p><p class="p1">dictionary4 = [:]</p><p class="p2"><br/></p><p class="p1">/**</p><p class="p3"><span class="s1"> * </span>字典的基本操作</p><p class="p1"> */</p><p class="p3"><span class="s1">//</span>字典总数</p><p class="p1">var dictionary = [1:"a", 2:"b", 3:"c"]</p><p class="p1">dictionary.count</p><p class="p3"><span class="s1">//</span>字典是否为空</p><p class="p1">dictionary.isEmpty</p><p class="p3"><span class="s1">//</span>调用,传入的<span class="s1">key</span>可以是任意值,程序不会报错,因为返回的是<span class="s1">optional</span>。<span class="s1">key</span>不存在时返回<span class="s1">nil</span></p><p class="p1">dictionary[1]</p><p class="p1">"<span class="s2">第一个元素:</span>" + dictionary[1]!</p><p class="p3"><span class="s1">//</span>插入新元素</p><p class="p1">dictionary[4] = "d"</p><p class="p3"><span class="s1">//</span>修改元素<span class="s1"> </span>两种方式</p><p class="p1">dictionary[4] = "e"</p><p class="p1">var oldValue = dictionary.updateValue("e", forKey: 4)</p><p class="p3"><span class="s1">//</span>删除元素<span class="s1"> </span>两种方式</p><p class="p1">dictionary[4] = nil</p><p class="p1">var oldValue4 = dictionary.removeValueForKey(4)</p><p class="p3"><span class="s1">//</span>字典的遍历</p><p class="p1">for (key, value) in dictionary{</p><p class="p1"> println("\\(key) -> \\(value)")</p><p class="p1">}</p><p class="p1">//<span class="s2">遍历</span>key</p><p class="p1">Array(dictionary.keys)</p><p class="p1">[Int](dictionary.keys)</p><p class="p1">//<span class="s2">遍历</span>value</p><p class="p1">Array(dictionary.values)</p><p class="p1">[String](dictionary.values)</p>', 1420777152, 1, 0, 0),
(86, 12, '李轩Lane', '初探Swift四 - 条件控制', 'Swift条件控制,有for,for...in...,switch,if...else,while等,而跳出条件控制有break,continue,fallthrough。本篇文章变分析这几个控制的特性。', 'Swift条件控制_Swift循环_Swift跳出循环_初探Swift', 'Swift条件控制,有for,for...in...,switch,if...else,while等,而跳出条件控制有break,continue,fallthrough。本篇文章变分析这几个控制的特性。', 'Swift条件控制,Swift循环,Swift跳出循环', 'swift|ios', 680, '<p class="p1"> Swift条件控制,有for,for...in...,switch,if...else,while等,而跳出条件控制有break,continue,fallthrough。本篇文章变分析这几个控制的特性。</p><p class="p1"> for in相关的如下:</p><p class="p1">[code]</p><p class="p1">/**</p><p class="p1">* for - in <span class="s1">循环</span></p><p class="p1">*/</p><p class="p1">//for<span class="s1">循环</span> >=-10 <=10</p><p class="p2"><span class="s2">for</span> i <span class="s2">in</span> -<span class="s3">10</span>...<span class="s3">10</span>{</p><p class="p2"> i*i</p><p class="p2">}</p><p class="p3"><span class="s4">//</span>遍历一个数组</p><p class="p2"><span class="s2">var</span> arr = [<span class="s5">"a"</span>, <span class="s5">"b"</span>, <span class="s5">"c"</span>]</p><p class="p2"><span class="s2">for</span> a <span class="s2">in</span> <span class="s6">arr</span>{</p><p class="p2"> <span class="s7">println</span>(a)</p><p class="p2">}</p><p class="p2"><span class="s2">for</span> (key, value) <span class="s2">in</span> <span class="s7">enumerate</span>(<span class="s6">arr</span>){</p><p class="p2"> <span class="s7">println</span>(<span class="s5">"</span>\\(key)<span class="s5">:</span>\\(value)<span class="s5">"</span>)</p><p class="p2">}</p><p class="p3"><span class="s4">//</span>遍历一个字典</p><p class="p4"><span class="s2">var</span><span class="s8"> dict = [</span>"a"<span class="s8">:</span>"A"<span class="s8">, </span>"b"<span class="s8">:</span>"B"<span class="s8">, </span>"c"<span class="s8">:</span>"C"<span class="s8">]</span></p><p class="p2"><span class="s2">for</span> (key, value) <span class="s2">in</span> <span class="s6">dict</span>{</p><p class="p2"> <span class="s7">println</span>(<span class="s5">"</span>\\(key)<span class="s5">:</span>\\(value)<span class="s5">"</span>)</p><p class="p2">}</p><p class="p5">[/code]</p><p class="p1" style="white-space: normal;"> for相关的如下:</p><p class="p1" style="white-space: normal;">[code]</p><p class="p1">/**</p><p class="p1">* for <span class="s1">循环</span></p><p class="p1">*/</p><p class="p1">//<span class="s1">等同</span>for var i = -100; i<=100; i++</p><p class="p2"><span class="s2">var</span> i = -<span class="s3">10</span></p><p class="p2"><span class="s2">for</span> ; <span class="s6">i</span><=<span class="s3">10</span>; <span class="s6">i</span>++</p><p class="p2">{</p><p class="p2"> <span class="s6">i</span>*<span class="s6">i</span></p><p class="p2">}</p><p class="p2">[/code]</p><p class="p1" style="white-space: normal;"> if相关的如下:</p><p class="p1" style="white-space: normal;">[code]</p><p class="p1">//if else</p><p class="p6">if<span class="s8"> </span>true<span class="s8"> {</span></p><p class="p5"> </p><p class="p6"><span class="s8">}</span>else<span class="s8">{</span></p><p class="p5"> </p><p class="p2">}</p><p class="p2" style="white-space: normal;">[/code]</p><p class="p1" style="white-space: normal;"> switch相关的如下:</p><p class="p1" style="white-space: normal;">[code]</p><p class="p1">/**</p><p class="p1"> *switch,<span class="s1">可以对</span>Int<span class="s1">,</span>String<span class="s1">,</span>Bool<span class="s1">,</span>Float<span class="s1">,</span>Double<span class="s1">进行判断</span></p><p class="p1"> *</p><p class="p1"> *<span class="s1">不需要显式写</span>break;</p><p class="p1"> */</p><p class="p2"><span class="s2">var</span> switchValue = <span class="s5">"a"</span></p><p class="p7"><span class="s2">switch</span><span class="s8"> </span>switchValue<span class="s8"> {</span></p><p class="p2"> <span class="s2">case</span> <span class="s5">"a"</span>:</p><p class="p2"> <span class="s7">println</span>(<span class="s5">"a"</span>)</p><p class="p2"> <span class="s2">case</span> <span class="s5">"b"</span>:</p><p class="p2"> <span class="s7">println</span>(<span class="s5">"b"</span>)</p><p class="p6"><span class="s8"> </span>default<span class="s8">:</span></p><p class="p2"> <span class="s7">println</span>(<span class="s5">"d"</span>)</p><p class="p2">}</p><p class="p3"><span class="s4">//</span>判断多个值不能写:</p><p class="p1">//case "a":</p><p class="p1">//case "A":</p><p class="p1">// println("ok");</p><p class="p3"><span class="s4">//</span>而是应该写:</p><p class="p1">//case "a", "A":</p><p class="p2" style="white-space: normal;">[/code]</p><p class="p1" style="white-space: normal;"> switch高级特性相关的如下:</p><p class="p1" style="white-space: normal;">[code]</p><p class="p1" style="white-space: normal;">/**</p><p class="p1"> * switch<span class="s1">高级特性</span></p><p class="p1"> */</p><p class="p1">//<span class="s1">元组在</span>switch<span class="s1">中</span></p><p class="p3"><span class="s4">//</span>如果想执行第一个<span class="s4">case</span>后还想执行第二个<span class="s4">case</span>,则添加<span class="s4">fallthrough</span>,此时直接进入下面的语句,不进行<span class="s4">case</span>判断</p><p class="p2"><span class="s2">var</span> request = (<span class="s2">true</span>, <span class="s5">"success"</span>)</p><p class="p7"><span class="s2">switch</span><span class="s8"> </span>request<span class="s8">{</span></p><p class="p1"><span class="s8"> </span>//<span class="s1">这个是正确的</span></p><p class="p2"> <span class="s2">case</span> (<span class="s2">true</span>, <span class="s5">"success"</span>):</p><p class="p4"><span class="s8"> </span><span class="s7">println</span><span class="s8">(</span>"true, success"<span class="s8">)</span></p><p class="p6"><span class="s8"> </span>fallthrough</p><p class="p3"><span class="s9"> </span><span class="s4">//</span>这个也是正确的,可以忽略元组的第一个元素,将剩下的元素判断</p><p class="p2"> <span class="s2">case</span> (<span class="s2">true</span>, <span class="s2">_</span>):</p><p class="p4"><span class="s8"> </span><span class="s7">println</span><span class="s8">(</span>"true, success"<span class="s8">)</span></p><p class="p3"><span class="s9"> </span><span class="s4">//</span>这个也是正确的</p><p class="p2"> <span class="s2">case</span> (<span class="s2">true</span>, <span class="s2">let</span> requestStatus):</p><p class="p2"> <span class="s7">println</span>(<span class="s5">"</span><span class="s10">当前登陆状态为:</span>\\(requestStatus)<span class="s5">"</span>);</p><p class="p6"><span class="s8"> </span>default<span class="s8">:</span></p><p class="p4"><span class="s8"> </span><span class="s7">println</span><span class="s8">(</span>"not found!"<span class="s8">)</span></p><p class="p2">}</p><p class="p1">//switch<span class="s1">可以比较范围</span></p><p class="p2"><span class="s2">var</span> request2 = (<span class="s3">5</span>, <span class="s3">12</span>);</p><p class="p7"><span class="s2">switch</span><span class="s8"> </span>request2<span class="s8">{</span></p><p class="p2"> <span class="s2">case</span> (<span class="s3">1</span>...<span class="s3">8</span>, <span class="s3">10</span>...<span class="s3">20</span>):</p><p class="p2"> <span class="s7">println</span>(<span class="s5">"ok"</span>)</p><p class="p6"><span class="s8"> </span>default<span class="s8">:</span></p><p class="p4"><span class="s8"> </span><span class="s7">println</span><span class="s8">(</span>"not found!"<span class="s8">)</span></p><p class="p2">}</p><p class="p1">//switch<span class="s1">的</span>case<span class="s1">中可以增加逻辑判断</span></p><p class="p2"><span class="s2">var</span> request3 = (<span class="s3">3</span>, <span class="s3">3</span>)</p><p class="p7"><span class="s2">switch</span><span class="s8"> </span>request3<span class="s8">{</span></p><p class="p2"> <span class="s2">case</span> <span class="s2">let</span>(x, y) <span class="s2">where</span> x==y:</p><p class="p2"> <span class="s7">println</span>(<span class="s5">"</span>\\(x)<span class="s5">-></span>\\(y)<span class="s5">"</span>)</p><p class="p6"><span class="s8"> </span>default<span class="s8">:</span></p><p class="p4"><span class="s8"> </span><span class="s7">println</span><span class="s8">(</span>"not found!"<span class="s8">)</span></p><p class="p2">}</p><p class="p2" style="white-space: normal;">[/code]</p><p class="p1" style="white-space: normal;"> 控制转移相关的如下,break只能跳出一层循环,如果要跳出多层,可以看下面的例子。另外break只能跳出循环,而不是花括号得代码块,比如if:</p><p class="p1" style="white-space: normal;">[code]</p><p class="p1">/**</p><p class="p3"><span class="s4"> * </span>控制转移</p><p class="p1"> * fallthrough<span class="s1">、</span>break<span class="s1">、</span>continue</p><p class="p1"> */</p><p class="p2"><span class="s2">import</span> UIKit</p><p class="p2"><span class="s2">var</span> board = <span class="s11">Array</span><<span class="s11">Array</span><<span class="s11">Int</span>>>()</p><p class="p2"><span class="s2">for</span> i <span class="s2">in</span> <span class="s3">0</span>...<span class="s3">10</span>{</p><p class="p2"> <span class="s6">board</span>.<span class="s7">append</span>(<span class="s11">Array</span>(count:<span class="s3">10</span>, repeatedValue:<span class="s3">0</span>))</p><p class="p2">}</p><p class="p2"><span class="s2">let</span> x = <span class="s11">Int</span>(arc4random()%<span class="s3">10</span>)</p><p class="p2"><span class="s2">let</span> y = <span class="s11">Int</span>(arc4random()%<span class="s3">10</span>)</p><p class="p2"><span class="s6">board</span>[<span class="s6">x</span>][<span class="s6">y</span>] = <span class="s3">1</span></p><p class="p7">board</p><p class="p2"><span class="s2">var</span> i = <span class="s3">0</span>, j = <span class="s3">0</span></p><p class="p2">mainloop:<span class="s2">for</span> <span class="s6">i</span> = <span class="s3">0</span>; <span class="s6">i</span><<span class="s3">10</span>; <span class="s6">i</span>++ {</p><p class="p2"> <span class="s2">for</span> <span class="s6">j</span>=<span class="s3">0</span>; <span class="s6">j</span> < <span class="s3">10</span>; <span class="s6">j</span>++ {</p><p class="p2"> <span class="s2">if</span> <span class="s6">board</span>[<span class="s6">i</span>][<span class="s6">j</span>] == <span class="s3">1</span> {</p><p class="p2"> <span class="s2">break</span> mainloop</p><p class="p2"> }</p><p class="p2"> }</p><p class="p2">}</p><p class="p2"><span class="s7">println</span>(<span class="s5">"</span>\\(<span class="s6">i</span>)<span class="s5"> - </span>\\(<span class="s6">j</span>)<span class="s5">"</span>)</p>', 1420797562, 0, 0, 0),
(87, 12, '李轩Lane', '初探Swift五 - 函数', '初探Swift,Swift函数,函数的声明、使用、特性等。本文包括Swift的函数创建、调用、传参、嵌套,已经返回函数的函数。函数不止对Swift重要,在任何一门语言中,函数都是最重要的。', 'Swift函数_Swift函数使用_Swift函数特性_初探Swift', '初探Swift,Swift函数,函数的声明、使用、特性等。本文包括Swift的函数创建、调用、传参、嵌套,已经返回函数的函数。函数不止对Swift重要,在任何一门语言中,函数都是最重要的。', 'Swift函数,Swift函数使用,Swift函数特性,初探Swift', 'swift|ios', 520, '<p class="p1">/**</p><p class="p2"><span class="s1"> * </span>创建函数</p><p class="p1"> * <span class="s2">参数是</span>String<span class="s2">,返回值是</span>String<span class="s2">。其中参数和</span>返回值<span class="s2">都是可选的,但是如果有参数,或者有return,则是必须的</span></p><p class="p1"> */</p><p class="p3"><span class="s3">func</span> sayHello(name:<span class="s4">String</span>)-><span class="s4">String</span>{</p><p class="p3"> <span class="s3">return</span> <span class="s5">"Hello "</span>+name+<span class="s5">"."</span></p><p class="p3">}</p><p class="p4"><span class="s6">println</span><span class="s7">(</span>sayHello<span class="s7">(</span><span class="s5">"lane"</span><span class="s7">))</span></p><p class="p5"><br/></p><p class="p3"><span class="s3">func</span> sayHelloOptional(name:<span class="s4">String</span>?)-><span class="s4">String</span>{</p><p class="p3"> <span class="s3">var</span> result = <span class="s5">"Hello,"</span> + (name ?? <span class="s5">"Guest"</span>)</p><p class="p3"> <span class="s3">return</span> result</p><p class="p3">}</p><p class="p3"><span class="s3">var</span> name:<span class="s4">String</span>?</p><p class="p4"><span class="s6">println</span><span class="s7">(</span>sayHelloOptional<span class="s7">(</span><span class="s8">name</span><span class="s7">))</span></p><p class="p5"><br/></p><p class="p1">/**</p><p class="p2"><span class="s1"> * </span>函数和元组</p><p class="p1"> * <span class="s2">参数是</span>String<span class="s2">,返回值是</span>String<span class="s2">。其中参数和</span>返回值<span class="s2">都是可选的</span></p><p class="p1"> */</p><p class="p3"><span class="s3">func</span> maxMinScores( scores:[<span class="s4">Int</span>] )->( maxScore:<span class="s4">Int</span>, minScore:<span class="s4">Int</span>)?{</p><p class="p3"> <span class="s3">if</span> scores.<span class="s4">isEmpty</span>{</p><p class="p3"> <span class="s3">return</span> <span class="s3">nil</span></p><p class="p3"> }</p><p class="p3"> <span class="s3">var</span> curmax = scores[<span class="s9">0</span>]</p><p class="p3"> <span class="s3">var</span> curmin = scores[<span class="s9">0</span>]</p><p class="p3"> <span class="s3">for</span> score <span class="s3">in</span> scores[<span class="s9">1</span>..<scores.<span class="s4">count</span>]{</p><p class="p3"> curmax = <span class="s6">max</span>(curmax, score)</p><p class="p3"> curmin = <span class="s6">min</span>(curmin, score)</p><p class="p3"> }</p><p class="p1"><span class="s7"> </span>//<span class="s2">两种返回方式</span></p><p class="p3"> <span class="s3">return</span> (maxScore:curmax, minScore:curmin)</p><p class="p1"><span class="s7"> </span>//<span class="s2">两种返回方式</span></p><p class="p1"><span class="s7"> </span>//return (curmax, curmin)</p><p class="p3">}</p><p class="p5"><br/></p><p class="p3"><span class="s3">var</span> userScores:[<span class="s4">Int</span>]? = [<span class="s9">12</span>, <span class="s9">990</span>, <span class="s9">572</span>, <span class="s9">3258</span>, <span class="s9">9999</span>, <span class="s9">1204</span>]</p><p class="p6">userScores<span class="s7"> = </span>userScores<span class="s7"> ?? []</span></p><p class="p3"><span class="s3">if</span> <span class="s3">let</span> maxMin = <span class="s10">maxMinScores</span>(<span class="s8">userScores</span>!){</p><p class="p3"> maxMin.maxScore</p><p class="p3"> maxMin.minScore</p><p class="p3">}</p><p class="p5"><br/></p><p class="p1">/**</p><p class="p2"><span class="s1"> * </span>值传递、引用、默认情况</p><p class="p1"> */</p><p class="p2"><span class="s1">//</span>默认下不能对参数修改,只能读取。此时参数被称为常数参数</p><p class="p3"><span class="s3">func</span> paramTest(varName:<span class="s4">Int</span>)-><span class="s4">Int</span>{</p><p class="p3"> <span class="s3">var</span> sum = varName*<span class="s9">2</span>;</p><p class="p2"><span class="s11"> </span><span class="s1">//</span>报错,不能对参数修改,只能读</p><p class="p1"><span class="s7"> </span>//varName--;</p><p class="p3"> <span class="s3">return</span> sum</p><p class="p3">}</p><p class="p2"><span class="s1">//</span>值传递,可读可写,变量前面加一个<span class="s1">var</span></p><p class="p3"><span class="s3">func</span> paramTest1(<span class="s3">var</span> varName:<span class="s4">Int</span>)-><span class="s4">Int</span>{</p><p class="p3"> <span class="s3">var</span> sum = varName*<span class="s9">2</span>;</p><p class="p2"><span class="s11"> </span><span class="s1">//</span>这个时候下句不报错了。</p><p class="p3"> varName--;</p><p class="p3"> <span class="s3">return</span> sum</p><p class="p3">}</p><p class="p2"><span class="s1">//</span>引用,可读可写,变量前面加一个<span class="s1">inout</span>,使用时加<span class="s1">&</span></p><p class="p3"><span class="s3">func</span> paramTest2(<span class="s3">inout</span> varName:<span class="s4">Int</span>)-><span class="s4">Int</span>{</p><p class="p3"> <span class="s3">var</span> sum = varName*<span class="s9">2</span>;</p><p class="p2"><span class="s11"> </span><span class="s1">//</span>这个时候下句不报错了。</p><p class="p3"> varName--;</p><p class="p3"> <span class="s3">return</span> sum</p><p class="p3">}</p><p class="p3"><span class="s3">var</span> param = <span class="s9">10</span>;</p><p class="p4">paramTest<span class="s7">(</span><span class="s8">param</span><span class="s7">)</span></p><p class="p1">//param = 10</p><p class="p4">paramTest1<span class="s7">(</span><span class="s8">param</span><span class="s7">)</span></p><p class="p1">//param = 10</p><p class="p4">paramTest2<span class="s7">(&</span><span class="s8">param</span><span class="s7">)</span></p><p class="p1">//param = 9</p><p class="p5"><br/></p><p class="p5"><br/></p><p class="p1">/**</p><p class="p2"><span class="s1"> * </span>函数类型</p><p class="p1"> */</p><p class="p3"><span class="s3">func</span> add(a:<span class="s4">Int</span>, b:<span class="s4">Int</span>)-><span class="s4">Int</span>{</p><p class="p3"> <span class="s3">return</span> a+b</p><p class="p3">}</p><p class="p2"><span class="s1">//</span>隐式声明一个函数类型</p><p class="p3"><span class="s3">let</span> funcVar = <span class="s10">add</span></p><p class="p3"><span class="s10">add</span>(<span class="s9">3</span>, <span class="s9">4</span>)</p><p class="p2"><span class="s1">//</span>显式声明一个函数类型</p><p class="p3"><span class="s3">let</span> funcVar2:(<span class="s4">Int</span>, <span class="s4">Int</span>)-><span class="s4">Int</span> = <span class="s10">add</span></p><p class="p2"><span class="s1">//</span>显式声明一个不需要参数和返回值的函数类型</p><p class="p3"><span class="s3">func</span> voidFunc(){</p><p class="p7"><span class="s7"> </span><span class="s6">println</span><span class="s7">(</span>"hello world"<span class="s7">)</span></p><p class="p3">}</p><p class="p1">//let funcVar3:()->() = voidFunc</p><p class="p3"><span class="s3">let</span> funcVar4:()-><span class="s4">Void</span> = <span class="s10">voidFunc</span></p><p class="p5"><br/></p><p class="p1">/**</p><p class="p2"><span class="s1"> * </span>函数嵌套</p><p class="p1"> */</p><p class="p3"><span class="s3">func</span> sum(a:<span class="s4">Int</span>, b:<span class="s4">Int</span>)-><span class="s4">Int</span>{</p><p class="p3"> <span class="s3">func</span> num(c:<span class="s4">Int</span>)-><span class="s4">Int</span>{</p><p class="p3"> <span class="s3">return</span> c*c</p><p class="p3"> }</p><p class="p3"> <span class="s3">return</span> <span class="s10">num</span>(a) + <span class="s10">num</span>(b)</p><p class="p3">}</p><p class="p3"><span class="s10">sum</span>(<span class="s9">10</span>, <span class="s9">20</span>)</p><p class="p5"><br/></p><p class="p1">/**</p><p class="p2"><span class="s1"> * </span>返回函数类型的函数</p><p class="p1"> */</p><p class="p3"><span class="s3">func</span> func1(a:<span class="s4">Int</span>)-><span class="s4">String</span>{</p><p class="p3"> <span class="s3">return</span> <span class="s5">"</span><span class="s12">结果是</span><span class="s5">"</span>+<span class="s4">String</span>(a * a)</p><p class="p3">}</p><p class="p3"><span class="s3">func</span> func2(a:<span class="s4">Int</span>)-><span class="s4">String</span>{</p><p class="p3"> <span class="s3">return</span> <span class="s5">"</span><span class="s12">结果是</span><span class="s5">"</span>+<span class="s4">String</span>(a + a)</p><p class="p3">}</p><p class="p3"><span class="s3">func</span> choseFunc(a:<span class="s4">Int</span>)->(<span class="s4">Int</span>)-><span class="s4">String</span>{</p><p class="p3"> <span class="s3">var</span> result:(<span class="s4">Int</span>)-><span class="s4">String</span></p><p class="p3"> <span class="s3">if</span> a%<span class="s9">2</span>==<span class="s9">0</span> {</p><p class="p3"> result = <span class="s10">func1</span></p><p class="p3"> }<span class="s3">else</span>{</p><p class="p3"> result = <span class="s10">func2</span></p><p class="p3"> }</p><p class="p3"> <span class="s3">return</span> result</p><p class="p3">}</p><p class="p3"><span class="s3">var</span> funcName = <span class="s10">choseFunc</span>(<span class="s9">10</span>)</p><p class="p6">funcName<span class="s7">(</span><span class="s9">10</span><span class="s7">)</span></p><p class="p4"><span class="s8">funcName</span><span class="s7"> = </span>choseFunc<span class="s7">(</span><span class="s9">5</span><span class="s7">)</span></p><p class="p6">funcName<span class="s7">(</span><span class="s9">5</span><span class="s7">)</span></p><p class="p5"><br/></p><p class="p5"><br/></p><p><br/></p>', 1421045986, 0, 0, 0),
(88, 12, '李轩Lane', '初探Swift六 - 闭包', '初探Swift,本文关于Swift闭包、Swift闭包特性(比如结尾闭包和捕获参数)、Swift闭包引用类型的相关介绍与实践。swift闭包一个很重要的特性,可以简化代码,优化流程。使代码更加简洁。', 'Swift闭包_Swift闭包特性_Swift闭包引用类型_初探Swift', '初探Swift,本文关于Swift闭包、Swift闭包特性(比如结尾闭包和捕获参数)、Swift闭包引用类型的相关介绍与实践。swift闭包一个很重要的特性,可以简化代码,优化流程。使代码更加简洁。', 'Swift闭包,Swift闭包特性,Swift闭包引用类型,初探Swift', 'swift|ios', 856, '<p class="p1"><span class="s1"> 初探Swift,本文关于Swift闭包、Swift闭包特性(比如结尾闭包和捕获参数)、Swift闭包引用类型的相关介绍与实践。swift闭包一个很重要的特性,可以简化代码,优化流程。使代码更加简洁。</span></p><p class="p1"><span class="s1"> 一、闭包<span class="s2">(Closure)</span>,将函数的声明和逻辑代码都放在参数的位置,闭包的本质还是函数。</span></p><p class="p1"><span class="s1">[code]</span></p><p class="p1"><span class="s1">import</span> UIKit</p><p class="p2">/**</p><p class="p4"><span class="s2"> * </span>闭包<span class="s2">(Closure)</span>,将函数的声明和逻辑代码都放在参数的位置,闭包的本质还是函数。</p><p class="p2"> */</p><p class="p2">var arr = [2, 1, 5, 3, 9, 0, 4]</p><p class="p4"><span class="s2">//</span>使用闭包将数组从大到小排列,<span class="s2">sorted</span>不穿第二个参数时默认为从下到大,闭包放在{}中,用()来声明<span class="s2">2</span>个变量,<span class="s2">-></span>来声明返回值,后面跟一个<span class="s2">in</span>,逻辑代码放在<span class="s2">in</span>后面。</p><p class="p2">var result = sorted(arr, {(a:Int, b:Int)->Bool in</p><p class="p2"> return a > b</p><p class="p2">})</p><p class="p2">result</p><p class="p4"><span class="s2">//</span>闭包的简化操作,闭包中<span class="s2">2</span>个参数的类型可以省略,返回值声明可以省略</p><p class="p2">result = sorted(arr, {a, b in return a > b})</p><p class="p2">result</p><p class="p4"><span class="s2">//</span>闭包进一步简化,省略<span class="s2">return</span></p><p class="p2">result = sorted(arr, {a, b in a>b})</p><p class="p2">result</p><p class="p4"><span class="s2">//</span>闭包再进一步优化,省略<span class="s2">a,b</span>两个参数声明,用<span class="s2">$0, $1</span>来代替,省略<span class="s2">in</span>关键字</p><p class="p2">result = sorted(arr, {$0 > $1})</p><p class="p4"><span class="s2">//</span>再省,因为<span class="s2">“>”</span>就是函数,是运算符函数</p><p class="p2">result = sorted(arr, >)</p><p class="p4"><span class="s2">//</span>注:必须是单行的才可以省略<span class="s2">return, in</span>已经<span class="s2">a</span>、<span class="s2">b</span>的声明</p><p class="p2">result</p><p class="p3">[/code]</p><p class="p3"> 二、结尾闭包,闭包的参数里面<span class="s2">{}</span>,看起来别扭,而且开发时可能会出现括号的对应问题。当传入闭包是最后一个参数的时候,可以使用如下的方式。仅仅是最后一个的时候可以!</p><p class="p3">[code]</p><p class="p2">/**</p><p class="p4"><span class="s2"> * </span>结尾闭包,闭包的参数里面<span class="s2">{}</span>,看起来别扭,而且开发时可能会出现括号的对应问题。当传入闭包是最后一个参数的时候,可以使用如下的方式。仅仅是最后一个的时候可以!</p><p class="p2"> */</p><p class="p2">result = sorted(arr){a, b in</p><p class="p2"> return a > b</p><p class="p2">}</p><p class="p2">result</p><p class="p3">[/code]</p><p class="p3"> 三、捕获参数(<span class="s2">capturing values</span>)我们可以在闭包中使用闭包外的变量</p><p class="p3">[code]</p><p class="p2">/**</p><p class="p4"><span class="s2"> * </span>捕获参数(<span class="s2">capturing values</span>)我们可以在闭包中使用闭包外的变量</p><p class="p2"> */</p><p class="p2">var num = 3</p><p class="p2">result = sorted(arr){</p><p class="p2"> return fabs(Float($0-num)) < fabs(Float($1-num))</p><p class="p2">}</p><p class="p2">result</p><p class="p2">[/code]</p><p class="p2"> 四、引用类型:将函数(闭包)赋值给函数类型的变量A,函数类型的变量A再赋值给函数类型的变量B,那么,A和B是指向同一块地址的,调用A引起的变化再变量B中将会体现</p><p class="p2">[code]</p><p class="p2">/**</p><p class="p4"><span class="s2"> * </span>引用类型</p><p class="p2"> */</p><p class="p1"><span class="s1">func</span> calcTotalMiles(todayMiles:<span class="s3">Int</span>)->()-><span class="s3">Int</span>{</p><p class="p1"> <span class="s1">var</span> totalMiles = <span class="s4">0</span></p><p class="p1"> <span class="s1">return</span> {totalMiles += todayMiles; <span class="s1">return</span> totalMiles;}</p><p class="p1">}</p><p class="p1"><span class="s1">var</span> dailyTwoMiles = <span class="s5">calcTotalMiles</span>(<span class="s4">2</span>)</p><p class="p2">//<span class="s6">结果:</span>2</p><p class="p6">dailyTwoMiles<span class="s7">()</span></p><p class="p2">//<span class="s6">结果:</span>4</p><p class="p6">dailyTwoMiles<span class="s7">()</span></p><p class="p4"><span class="s2">//</span>把函数复制给另一个变量</p><p class="p6"><span class="s1">var</span><span class="s7"> myPlan = </span>dailyTwoMiles</p><p class="p4"><span class="s2">//</span>调用这个变量</p><p class="p2">//<span class="s6">结果:</span>6</p><p class="p6">myPlan<span class="s7">()</span></p><p class="p2">//<span class="s6">结果:</span>8</p><p class="p6">myPlan<span class="s7">()</span></p><p class="p4"><span class="s2">//</span>回过头来调用之前被赋值的变量(函数类型的变量),看值是否改变</p><p class="p2">//<span class="s6">结果:</span>10</p><p class="p6">dailyTwoMiles<span class="s7">()</span></p><p class="p4"><span class="s2">//</span>说明这个是引用传递的,函数类型的变量<span class="s2">myPlan</span>和函数类型的变量<span class="s2">dailyTwoMiles</span>是指向同一个地址的。</p><p class="p4">[/code]</p><p><br/></p>', 1421116648, 0, 0, 0);
INSERT INTO `info_article` (`id`, `mid`, `author`, `title`, `description`, `seo_title`, `seo_description`, `seo_keywords`, `tag`, `clicks`, `content`, `ctime`, `good_num`, `bad_num`, `recommend_type`) VALUES
(89, 12, '李轩Lane', '初探Swift七 - 枚举', '初探Swift,Swift枚举。Swift枚举不同于C语言的枚举,C的枚举只能关联一个整形,而Swift枚举不但可以关联一个整形,更可以关联任意的数据类型。', 'Swift枚举_Swift枚举的定义和使用_初探Swift', '初探Swift,Swift枚举。Swift枚举不同于C语言的枚举,C的枚举只能关联一个整形,而Swift枚举不但可以关联一个整形,更可以关联任意的数据类型。', 'Swift枚举,Swift枚举的定义和使用,初探Swift', 'swift|ios', 1233, '<p> 初探Swift,Swift枚举。Swift枚举不同于C语言的枚举,C的枚举只能关联一个整形,而Swift枚举不但可以关联一个整形,更可以关联任意的数据类型。</p><p> 声明用enum开头,case 后面跟一个元素。可以不像C语言那样和整数关联。如下,ExamScore不是一个变量,而是一个数据类型,这个数据类型叫做ExamScore,同Int,String一样。</p><p>[code]</p><p>import UIKit</p><p>/**</p><p> * 枚举(Enumerations)</p><p> */</p><p>//声明用enum开头,case 后面跟一个元素。可以不像C语言那样和整数关联。如下,ExamScore不是一个变量,而是一个数据类型,这个数据类型叫做ExamScore,同Int,String一样。</p><p>enum ExamScore{</p><p> case score_a</p><p> case score_b</p><p> case score_c</p><p>}</p><p>var myScore:Int = 40</p><p>//声明一个变量是刚才声明的ExamScore类型</p><p>var rank:ExamScore</p><p>if myScore >= 90 {</p><p> //使用枚举:“枚举变量.枚举元素”</p><p> rank = ExamScore.score_a</p><p>}else if myScore >= 60 {</p><p> //使用枚举:枚举变量可以省略,只要“.枚举元素”</p><p> rank = .score_b</p><p>}else{</p><p> rank = .score_c</p><p>}</p><p>rank</p><p>switch rank{</p><p> case .score_a:</p><p> println("A等成绩")</p><p> case .score_b:</p><p> println("B等成绩")</p><p> case .score_c:</p><p> println("C等成绩")</p><p>}</p><p><br/></p><p>/**</p><p> * 枚举 - 关联一个值</p><p> */</p><p>enum Month:Int{</p><p> case Jan=1, Feb, Mar</p><p>}</p><p>//声明了Jan=1,Feb和Mar系统会自动赋值为2和3</p><p>//使用</p><p>//此时值为1</p><p>Month.Jan.rawValue</p><p>//另一种好似用方式</p><p>var myMonth:Month</p><p>myMonth = .Jan</p><p>myMonth.rawValue</p><p>//寻找关联值为2的那个枚举元素,返回一个可选型,因为如果寻找1000的那个元素,是找不到。</p><p>var nextMonth = Month(rawValue: 2)</p><p>nextMonth!.rawValue</p><p>//不仅仅,还可以关联字符串</p><p>enum MonthString:String{</p><p> case Jan="一月"</p><p> case Feb="二月"</p><p> case Mar="三月"</p><p>}</p><p>//一月</p><p>MonthString.Jan.rawValue</p><p><br/></p><p><br/></p><p>/**</p><p> * 复杂的枚举</p><p> */</p><p>enum UserInfo{</p><p> case birthday(Int, Int, Int)</p><p> case name(String)</p><p>}</p><p>let xiaomingBirthday = UserInfo.birthday(1990, 1, 10)</p><p>let xiaomingName:UserInfo = .name("小明")</p><p><br/></p><p>switch xiaomingBirthday{</p><p> case .birthday(let year, let month, let day):</p><p> println("\\(year)年\\(month)月\\(day)")</p><p> case .name(let name):</p><p> println("大家号,我叫\\(name)")</p><p>}</p><p>[code]<br/></p>', 1421388139, 1, 0, 0),
(90, 12, '李轩Lane', '初探Swift八 - 类', '初探Swift,Swift类,Swift类的实例化,Swift类的操作等是本文的内容。这是一门强类型、完全面相对象的语言。类可以更好的封装、抽象、复用模块和代码,使得代码更加的优雅简洁。', 'Swift类_Swift类的实例化_Swift类的操作_初探Swift', '初探Swift,Swift类,Swift类的实例化,Swift类的操作等是本文的内容。这是一门强类型、完全面相对象的语言。类可以更好的封装、抽象、复用模块和代码,使得代码更加的优雅简洁。', 'Swift类,Swift类的实例化,Swift类的操作,初探Swift', 'swift|ios', 2315, '<p> 初探Swift,Swift类,Swift类的实例化,Swift类的操作等是本文的内容。这是一门强类型、完全面相对象的语言。类可以更好的封装、抽象、复用模块和代码,使得代码更加的优雅简洁。</p><p>[code]</p><p>/**</p><p> * 类</p><p> */</p><p>//定义一个类</p><p>class TV{</p><p> //定义属性</p><p> var price = 1999</p><p> var name = "乐TV"</p><p> //定义方法</p><p> func open()->String{</p><p> return "Success"</p><p> }</p><p> func close()->Bool{</p><p> return true</p><p> }</p><p>}</p><p>//创建对象,不需要new这个关键字的</p><p>var myTV = TV()</p><p>//调用类的属性</p><p>println(myTV.name)</p><p>//调用类的方法</p><p>myTV.open()</p><p>myTV.close()</p><p>//类的对象是值调用还是引用调用。</p><p>var myTV2 = myTV</p><p>//改变myTV2的价格</p><p>myTV2.price = 2000</p><p>//打印myTV2的价格,是2000,修改成功</p><p>myTV2.price</p><p>//打印myTV1的价格,是2000,说明是引用传递的</p><p>myTV.price</p><p><br/></p><p>//可以用“===”来判断使用来自同一个类</p><p>myTV === myTV2</p><p><br/></p><p>//swift是没有指针概念的:因为虽然引用传递用的还是指针,但是swift并没有给我们指针的语法</p><p><br/></p><p>/**</p><p> * 类的构造函数、析构函数</p><p> */</p><p>class People{</p><p> var name:String = "小明"</p><p>}</p><p>var peopleObj = People()</p><p>//结果是小明</p><p>peopleObj.name</p><p>//增加析构函数后</p><p>class People2{</p><p> var name:String = "小明"</p><p> //析构函数,系统特殊函数,不需要加func</p><p> init(name:String){</p><p> self.name = name</p><p> }</p><p> //析构函数,系统特殊函数,不需要加func和()</p><p> deinit {</p><p> name = ""</p><p> }</p><p>}</p><p>var peopleObj2 = People2(name: "小红")</p><p>peopleObj2.name</p><p><br/></p><p><br/></p><p>/**</p><p> * 类的继承</p><p> */</p><p>//学生类继承人类类</p><p>class Student : People{}</p><p>var studentObj = Student()</p><p>studentObj.name</p><p><br/></p><p>class Student2 : People2{</p><p> override init(name:String){</p><p> //父类的构造函数需要手动调用,不会自动执行</p><p> super.init(name: "小红")</p><p> super.name = "hello: \\(super.name)"</p><p> self.name = name</p><p> }</p><p> //重写父类的函数需要加关键词override</p><p> </p><p> //重写属性</p><p> override var name:String{</p><p> get{</p><p> return super.name</p><p> }</p><p> set{</p><p> super.name = "hi \\(name)"</p><p> }</p><p> }</p><p> </p><p> //不希望被重写,再父类的方法前面加@final。可以加在方法、属性、设置类前面</p><p>}</p><p>var studentObj2 = Student2(name: "小白")</p><p>println(studentObj2.name)</p><p>studentObj2.name = "lane"</p><p>println(studentObj2.name)</p><p>[/code]</p>', 1421553330, 3, 2, 0),
(91, 12, '李轩Lane', '初探Swift九 - 泛型与结构体', '初探Swift,Swift结构体是一种数据类型,与C语言的结构体类型,成员可以是各种数据类型,甚至是函数。Swift泛型是广泛的类型,即函数的参数中一个变量声明为Int,它也可以接受String并处理。', 'Swift结构体_Swift泛型_初探Swift', '初探Swift,Swift结构体是一种数据类型,与C语言的结构体类型,成员可以是各种数据类型,甚至是函数。Swift泛型是广泛的类型,即函数的参数中一个变量声明为Int,它也可以接受String并处理。', 'Swift结构体,Swift泛型,初探Swift', 'swift|ios', 840, '<p> 初探Swift,Swift结构体是一种数据类型,与C语言的结构体类型,成员可以是各种数据类型,甚至是函数。Swift泛型是广泛的类型,即函数的参数中一个变量声明为Int,它也可以接受String并处理。</p><p><br/></p><p> 一、结构体,声明与使用:</p><p>[code]</p><p>/**</p><p> * 结构体</p><p> */</p><p>//声明一个结构体,结构体带有默认值</p><p>struct People {</p><p> var name = "小明"</p><p> var age = 0</p><p> func getKeyNumber()->Int{</p><p> return age</p><p> }</p><p>}</p><p>var xiaoming = People()</p><p>xiaoming.getKeyNumber()</p><p>var lane = People(name: "Lane", age: 24)</p><p>lane.getKeyNumber()</p><p>[code]</p><p><br/></p><p> 二、泛型:在一个函数中,比如在交换两个变量值的函数中,声明时定义了Int型,那如果我想交换两个String时,怎么办?</p><p>[code]</p><p>/**</p><p>* 泛型</p><p>* 比如在交换两个变量值的函数中,声明时定义了Int型,那如果我想交换两个String时,怎么办?</p><p>*/</p><p>func swapValue(inout a:Int, inout b:Int){</p><p> let temp = a</p><p> a = b</p><p> b = temp</p><p>}</p><p>//引入泛型</p><p>func newSwapValue<T>(inout a:T, inout b:T){</p><p> let temp = a</p><p> a = b</p><p> b = temp</p><p>}</p><p>//交换两个String</p><p>var a = "hello"</p><p>var b = "world"</p><p>a</p><p>b</p><p>newSwapValue(&a, &b)</p><p>a</p><p>b</p><p>//交换两个Int</p><p>var c = 1</p><p>var d = 2</p><p>c</p><p>d</p><p>newSwapValue(&c, &d)</p><p>c</p><p>d</p><p><br/></p><p>/**</p><p>* 类中定义泛型</p><p>*/</p><p>class MyT<T>{</p><p> func getName(s:T)->Void{ </p><p> println(s)</p><p> }</p><p>}</p><p><br/></p><p>var myTObj = MyT<Int>()</p><p>myTObj.getName(123)</p><p>var myTObj2 = MyT<String>()</p><p>myTObj2.getName("Hello world")</p><p>[/code]</p>', 1421633679, 0, 0, 0),
(92, 12, '李轩Lane', '初探Swif十 - 协议', '初探Swift,Swift协议,Swift接口,它是一种约定,实现这个接口的类必须要实现这个接口中所定义的所有方法。协议(Protocol),同PHP的接口(interface)。', 'Swift协议_Swift接口_初探Swift', '初探Swift,Swift协议,Swift接口,它是一种约定,实现这个接口的类必须要实现这个接口中所定义的所有方法。协议(Protocol),同PHP的接口(interface)。', 'Swift协议,Swift接口,初探Swift', 'swift|ios', 1282, '<p> 初探Swift,Swift协议,Swift接口,它是一种约定,实现这个接口的类必须要实现这个接口中所定义的所有方法。协议(Protocol),同PHP的接口(interface)。</p><p>/**</p><p> * 协议(Protocol),同PHP的接口(interface)</p><p> */</p><p>protocol MyProtocol{</p><p> //变量可读可写</p><p> var myName:String {</p><p> get set</p><p> }</p><p> //定义变量,只读</p><p> var myAge:Int{</p><p> get</p><p> }</p><p> func getMyName()->String</p><p>}</p><p>//继承的父类一定要写在最前面,然后用,分割,后面跟协议,多个协议用,分割</p><p>class MyClass : MyProtocol{</p><p> var m_name:String = ""</p><p> var myName:String {</p><p> get{</p><p> return "lane"</p><p> }set{</p><p> m_name = newValue</p><p> }</p><p> }</p><p> var myAge:Int{</p><p> get{</p><p> return 24</p><p> }</p><p> }</p><p> </p><p> func getMyName()->String{</p><p> return "hello Lane"</p><p> }</p><p>}</p><p><br/></p><p>/**</p><p>* 协议类型</p><p>*/</p><p>var varProtocol:MyProtocol = MyClass()</p><p>varProtocol.myName</p><p><br/></p><p>/**</p><p>* 协议继承</p><p>* 要实现子协议和父协议的全部内容才可以</p><p>*/</p><p>protocol ChildMyProtocol:MyProtocol{</p><p> func getMyAge()->Int</p><p>}</p><p>class MyClass2 : ChildMyProtocol{</p><p> var m_name:String = ""</p><p> var myName:String {</p><p> get{</p><p> return "lane"</p><p> }set{</p><p> m_name = newValue</p><p> }</p><p> }</p><p> var myAge:Int{</p><p> get{</p><p> return 24</p><p> }</p><p> }</p><p> </p><p> func getMyName()->String{</p><p> return "hello Lane"</p><p> }</p><p> </p><p> func getMyAge() -> Int {</p><p> return 20;</p><p> }</p><p>}</p><p><br/></p><p>/**</p><p>* 协议合并</p><p>*/</p><p>func hello(s:protocol<MyProtocol, ChildMyProtocol>)</p><p><br/></p>', 1421806484, 0, 0, 0),
(93, 1, '李轩Lane', 'PHP微信框架LaneWeChat入围2014年国人开源软件TOP100', '恭喜PHP微信框架LaneWeChat入围2014年国人开源软件TOP100,虽然第95名的成绩并不理想,但是对于开源开发者来说,是莫大的鼓励。', 'LaneWeChat_PHP微信框架_2014年国人开源软件TOP100', '恭喜PHP微信框架LaneWeChat入围2014年国人开源软件TOP100,虽然第95名的成绩并不理想,但是对于开源开发者来说,是莫大的鼓励。', 'LaneWeChat,PHP微信框架,2014年国人开源软件TOP100', 'LaneWeChat|PHP|微信|框架', 4149, '<p> 恭喜PHP微信框架LaneWeChat入围2014年国人开源软件TOP100,虽然第95名的成绩并不理想,但是对于开源开发者来说,是莫大的鼓励。</p><p> 榜单来自开源中国:<a href="http://www.oschina.net/news/58899/2014-cn-top-100-software" _src="http://www.oschina.net/news/58899/2014-cn-top-100-software">http://www.oschina.net/news/58899/2014-cn-top-100-software</a> </p><p>[code]</p><p style="margin-top: 0px; margin-bottom: 15pt; padding: 0px; font-family: Verdana, sans-serif, 宋体; font-size: 14px; line-height: 22.3999996185303px; white-space: normal; background-color: rgb(255, 255, 255);"><strong style="margin: 0px; padding: 0px;">95. LaneWeChat</strong></p><p style="margin-top: 0px; margin-bottom: 15pt; padding: 0px; font-family: Verdana, sans-serif, 宋体; font-size: 14px; line-height: 22.3999996185303px; white-space: normal; background-color: rgb(255, 255, 255);"><a target="_blank" href="http://www.oschina.net/p/lanewechat" rel="nofollow" style="margin: 0px; padding: 0px; color: rgb(62, 98, 166); outline: 0px;">LanWeChat</a>是微信PHP开发框架,快速开发微信公众号,以第三方代码包的形式引入即可。<br style="margin: 0px; padding: 0px;"/></p><p style="margin-top: 0px; margin-bottom: 15pt; padding: 0px; font-family: Verdana, sans-serif, 宋体; font-size: 14px; line-height: 22.3999996185303px; white-space: normal; background-color: rgb(255, 255, 255);">开发语言:PHP<br style="margin: 0px; padding: 0px;"/>授权协议:Do What The Fuck You Want To Public License<br style="margin: 0px; padding: 0px;"/>源码下载:<a target="_blank" href="http://git.oschina.net/lane/LaneWeChat" rel="nofollow" style="margin: 0px; padding: 0px; color: rgb(62, 98, 166); outline: 0px;">http://git.oschina.net/lane/LaneWeChat</a> <br style="margin: 0px; padding: 0px;"/>软件作者:<a href="http://my.oschina.net/u/938918" target="_blank" rel="nofollow" style="margin: 0px; padding: 0px; color: rgb(62, 98, 166); outline: 0px;">@李轩Lane</a></p><p>[/code]</p><p><br/></p><p> LaneWeChat是PHP微信快速开发框架,以第三方代码包的形式引入项目即可。封装微信各类接口,方便快捷的使用。无需阅读微信冗长的Wiki。</p><p><br/></p><p>官网:<a href="http://lanewechat.lanecn.com" _src="http://lanewechat.lanecn.com">http://lanewechat.lanecn.com</a> </p><p><br/></p><p>GitHub:<a href="https://github.com/lixuancn/LaneWeChat" _src="https://github.com/lixuancn/LaneWeChat">https://github.com/lixuancn/LaneWeChat</a></p><p><br/></p><p>欢迎Star,欢迎Fork,欢迎Pull Request</p>', 1427382872, 6, 0, 0),
(94, 1, '李轩Lane', 'PHP生成PDF文件', 'PHP生成PDF。使用TCPDF开源插件,实现PHP生成PDF文档。可以插入图片、HTML、链接、表格、柱状图折线图等PHP动态生成PDF的功能。', 'PHP生成PDF_PHP生成PDF文档_PHP动态生成PDF', 'PHP生成PDF。使用TCPDF开源插件,实现PHP生成PDF文档。可以插入图片、HTML、链接、表格、柱状图折线图等PHP动态生成PDF的功能。', 'PHP生成PDF,PHP生成PDF文档,PHP动态生成PDF', 'PHP|PDF', 2015, '<p> 本文介绍PHP生成PDF。我们使用TCPDF开源插件,实现PHP生成PDF文档。可以插入图片、HTML、链接、表格、柱状图折线图等PHP动态生成PDF的功能。</p><p> PHP的PECL扩展有一个叫做pdflib,并且维护到了2014年1月,PDFLib库对于个人是免费的,对于商业产品需要购买许可。并且使用相对复杂。因此排除。</p><p> 本文介绍一款插件,TCPDF!官网<a href="http://www.tcpdf.org。下载后在代码中引入即可使用。无需编译/安装其他的扩展。" _src="http://www.tcpdf.org。下载后在代码中引入即可使用。无需编译/安装其他的扩展。">http://www.tcpdf.org</a>。下载后在代码中引入即可使用。无需编译/安装其他的扩展。TCPDF的下载包和官网都会提供大量的示例和几十个字体(只是除个别外中文都不能用...)。采用LGPL license开源协议。</p><p> 下面直奔主题。在官网下载后。假设放在了/var/www/目录下。</p><p>[code]</p><p>//第一步肯定是引入TCPDF的入口文件</p><p>require_once '/var/www/tcpdf/tcpdf.php';</p><p><br/></p><p>//实例化</p><p>$pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false);</p><p>// 设置文档信息</p><p>$pdf->SetCreator('Lane');</p><p>$pdf->SetAuthor('Lane');</p><p>$pdf->SetTitle('PHP生成PDF');</p><p>$pdf->SetSubject('PHP动态生成PDF文件');</p><p>$pdf->SetKeywords('PHP PDF TCPDF');</p><p><br/></p><p>//设置页眉信息 参数分别是LOGO地址,LOGO大小,两行标题,标题颜色,分割线颜色。。颜色是RGB</p><p>$pdf->SetHeaderData('/var/www/tcpdf/examples/images/tcpdf_logo.jpg', 30, 'PHP生成PDF', 'PHP如何生成PDF文件', array(0,0,0), array(0,0,0));</p><p>//设置页脚信息</p><p>$pdf->setFooterData(array(0,0,0), array(0,0,0));</p><p>// 设置页眉和页脚字体</p><p>$pdf->setHeaderFont(Array('stsongstdlight', '', '12'));</p><p>$pdf->setFooterFont(Array('helvetica', '', '8'));</p><p>//设置默认等宽字体</p><p>$pdf->SetDefaultMonospacedFont('courier');</p><p>//设置间距</p><p>$pdf->SetMargins(15, 27, 15);</p><p>$pdf->SetHeaderMargin(5);</p><p>$pdf->SetFooterMargin(10);</p><p>//设置分页</p><p>$pdf->SetAutoPageBreak(TRUE, 15);</p><p>//设置图片比例</p><p>$pdf->setImageScale(1.25);</p><p>//将页眉页脚的信息输出出来。</p><p>$pdf->AddPage();</p><p><br/></p><p>//设置字体 - 正文标题的哦。B是加粗,15是大小</p><p>$pdf->SetFont('stsongstdlight', 'B', 15);</p><p>$pdf->Write(20, 'PHP如何动态生成PDF', '', 0, 'C', true, 0, false, false, 0);</p><p><br/></p><p>//设置字体 - 正文内容的哦。B是加粗,15是大小</p><p>$pdf->SetFont('stsongstdlight', '', 10);</p><p>//L是左对齐,R是右对齐,C是居中对齐。</p><p>$pdf->Write(0, $content,'', 0, 'L', true, 0, false, false, 0);</p><p><br/></p><p>//输出PDF。第二个参数默认是I,是浏览器预览。D是下载</p><p>$pdf->Output('PHP_TO_PDF.pdf', 'I');</p><p>[/code]</p><p><br/></p><p> 复制并执行上面的代码,会发现浏览器打开了PDF文件预览(如果你的浏览器不是IE的话)。把Output的第二个参数换成D,就可以下载了。</p><p><br/></p><p> 说到这里,基本是完成了。但是有个问题哦,你会发现字体很别扭,特别丑。我们可以换个字体,《droidsansfallback》。该字体并未自带。可以通过Google百度找到下载的地方,或者在<a href="http://sourceforge.net/projects/hawebs/files/Assistance/PHP/Droid%20Sans%20Fallback%20-%20PHP.zip/download" target="_blank" style="box-sizing: border-box; color: rgb(241, 110, 80); font-family: tahoma, helvetica, arial; font-size: 14px; line-height: 21px; orphans: 2; white-space: normal; widows: 2;">http://sourceforge.net/projects/hawebs/files/Assistance/PHP/Droid%20Sans%20Fallback%20-%20PHP.zip/download</a> 。 下载解压后,将droidsansfallback.php、droidsansfallback.z以及droidsansfallback.ctg.z这三个文件复制到 tcpdf/fonts/目录下。然后将代码中的stsongstdlight替换成droidsansfallback即可。在执行,就会发现字体好看多了。。当然,可以用TCPDF自带的tcpdf_addfont.php来将其他字体转换成TCPDF识别的字体,再移入tcpdf/fonts/目录下。比如微软雅黑什么的。</p><p><br/></p><p><br/></p>', 1439356956, 5, 10, 0);
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh" lang="zh" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="icon" href="./favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
<title>phpMyAdmin</title>
<link rel="stylesheet" type="text/css" href="phpmyadmin.css.php?token=0988a5284984c200bb2f4e7fc7dbf983&js_frame=right&nocache=4384684430" />
<link rel="stylesheet" type="text/css" href="print.css" media="print" />
<meta name="robots" content="noindex,nofollow" />
<script type="text/javascript">
//<![CDATA[
try {
// can't access this if on a different domain
var topdomain = top.document.domain;
// double-check just for sure
if (topdomain != self.document.domain) {
alert("Redirecting...");
top.location.replace(self.document.URL.substring(0, self.document.URL.lastIndexOf("/")+1));
}
}
catch(e) {
alert("Redirecting... (error: " + e);
top.location.replace(self.document.URL.substring(0, self.document.URL.lastIndexOf("/")+1));
}
//]]>
</script>
<script src="./js/tooltip.js" type="text/javascript"></script>
<script type="text/javascript">
// <![CDATA[
// Updates the title of the frameset if possible (ns4 does not allow this)
if (typeof(parent.document) != 'undefined' && typeof(parent.document) != 'unknown'
&& typeof(parent.document.title) == 'string') {
parent.document.title = 'pma.sinacloud.com / w.rdc.sae.sina.com.cn / app_lanecn / info_comment | phpMyAdmin 3.3.8.1';
}
var PMA_messages = new Array();
window.parent.addEvent(window, 'load', PMA_TT_init);
// ]]>
</script>
<meta name="OBGZip" content="true" />
<!--[if IE 6]>
<style type="text/css">
/* <![CDATA[ */
html {
overflow-y: scroll;
}
/* ]]> */
</style>
<![endif]-->
</head>
<body>
<div id="serverinfo">
<a href="main.php?token=0988a5284984c200bb2f4e7fc7dbf983" class="item"> <img class="icon" src="./themes/original/img/s_host.png" width="16" height="16" alt="" />
w.rdc.sae.sina.com.cn:3307</a>
<span class="separator"><img class="icon" src="./themes/original/img/item_ltr.png" width="5" height="9" alt="-" /></span>
<a href="db_structure.php?db=app_lanecn&token=0988a5284984c200bb2f4e7fc7dbf983" class="item"> <img class="icon" src="./themes/original/img/s_db.png" width="16" height="16" alt="" />
app_lanecn</a>
<span class="separator"><img class="icon" src="./themes/original/img/item_ltr.png" width="5" height="9" alt="-" /></span>
<a href="sql.php?db=app_lanecn&table=info_comment&token=0988a5284984c200bb2f4e7fc7dbf983" class="item"> <img class="icon" src="./themes/original/img/s_tbl.png" width="16" height="16" alt="" />
info_comment</a>
</div>
<!-- PMA-SQL-ERROR -->
<div class="error"><h1>错误</h1>
<p><strong>SQL 查询:</strong>
<a href="tbl_sql.php??sql_query=SHOW+TABLE+STATUS+FROM+%60app_lanecn%60+LIKE+%27info_comment%27&show_query=1&db=app_lanecn&table=info_comment&token=0988a5284984c200bb2f4e7fc7dbf983"><img src="./themes/original/img/b_edit.png" title="编辑" alt="编辑" class="icon" width="16" height="16" /></a> </p>
<p>
<span class="syntax"><span class="syntax_alpha syntax_alpha_reservedWord">SHOW</span> <span class="syntax_alpha syntax_alpha_reservedWord">TABLE</span> <span class="syntax_alpha syntax_alpha_reservedWord">STATUS</span> <span class="syntax_alpha syntax_alpha_reservedWord">FROM</span> <span class="syntax_quote syntax_quote_backtick">`app_lanecn`</span> <span class="syntax_alpha syntax_alpha_reservedWord">LIKE</span> <span class="syntax_quote syntax_quote_single">'info_comment'</span></span>
</p>
<p>
<strong>MySQL 返回:</strong><a href="http://dev.mysql.com/doc/refman/5.1/zh/error-messages-server.html" target="mysql_doc"><img class="icon" src="./themes/original/img/b_help.png" width="11" height="11" alt="文档" title="文档" /></a>
</p>
<code>
#2006 - MySQL server has gone away
</code><br />
</div><script type="text/javascript">
//<![CDATA[
// updates current settings
if (window.parent.setAll) {
window.parent.setAll('zh-utf-8', 'utf8_general_ci', '1', 'app_lanecn', 'info_comment', '0988a5284984c200bb2f4e7fc7dbf983');
}
// set current db, table and sql query in the querywindow
if (window.parent.reload_querywindow) {
window.parent.reload_querywindow(
'app_lanecn',
'info_comment',
'');
}
if (window.parent.frame_content) {
// reset content frame name, as querywindow needs to set a unique name
// before submitting form data, and navigation frame needs the original name
if (typeof(window.parent.frame_content.name) != 'undefined'
&& window.parent.frame_content.name != 'frame_content') {
window.parent.frame_content.name = 'frame_content';
}
if (typeof(window.parent.frame_content.id) != 'undefined'
&& window.parent.frame_content.id != 'frame_content') {
window.parent.frame_content.id = 'frame_content';
}
//window.parent.frame_content.setAttribute('name', 'frame_content');
//window.parent.frame_content.setAttribute('id', 'frame_content');
}
//]]>
</script>
</body>
</html>