Don't Repeat Yourself - 编程开发原则

1. Do not repeat yourself(重构、抽象...)

2. 工具能做的麻烦事,不要自己做(格式化、语法检查...)

IDE能做的事儿,不要浪费自己时间去做。
把时间用在代码逻辑、性能考虑上。

3. 接口方法,只做应该做的事,不多不乱,保持原数据结构/顺序。

例子:
任务页面有召集人(多个)的列表,是后台运营人员填写的,有时第一次访问时顺序有混乱,之后访问右正常了。觉得很奇怪,看代码知道了 原来是缓存搞的鬼!

背景:
微博的内容产品几乎都有好友/粉丝 与 物 的关系,所以通过array(uid,uid,uid,...)取用户详细信息的方法,是早已抽象好的,逻辑是:对传进来的array进行缓存查找,得到已缓存的条目未命中的uid列表,再以此查库将返回结果追加到已缓存的条目,返回结果。

策略不错,但有个小问题,参数传进来可能是有顺序的,所以期望的结果集的顺序应该是与参数一致的。
有两个办法,1改接口、2重新排序,因为作为调用方不便改接口,所以重新排序!

怎么排呢,经同事指正,几行代码:

$users = Dr_User::get_user_infos($uids, false);//结果集已经乱序了

$flip_users = array_flip($uids);//反转uid列表,array(uid=>0,uid=>1,...),再遍历users存到对应键上
foreach ($users as $user) {
    $flip_users[$user['id']] = $user;
}
$users = $flip_users;
unset($flip_users);

结论:如果很多调用方都有保持顺序的需求,接口的设计就该添加一个参数以控制结果是否保持顺序。

4. 务必打开所有错误显示,包括NOTICE级

5. 变量的检查,统一在view controller里做

6. 明确开发/生产环境...

...

Tips

  1. 新增/修改时,sql语句慎用replace,当字段中有主键和唯一键时,若已有记录,修改的实际操作是:将原纪录删除,新插入纪录,这显然不是期望的处理方式。
  2. 在做客户端API时,有时需要一个默认的图片card,不希望它跳转或作为普通图片可查看大图,只是让它作为默认显示(something like 背景图)
'pic_items' => array(
    '0' => array(
        'pic' => 'http://example.com/pic.jpg',
        'scheme'=>'sinaweibo://javascript:void(0);',
    ),
),

类似浏览器js的写法,scheme为sinaweibo://javascript:void(0);就好了。

堆排序\链表实现局部排序

以前面试时被问一个问题:有10万个乱序的数,要前5个最大(或最小)的数?
作为一个没好好学算法的人,还没有算法时间、空间复杂度的概念,只提出了冒泡、快速排序等,然后取前5。这显然不是合理的做法。

读了几本书,有一点点心得,下面介绍两个做法:
假设:输入为[31,5,12,24,41,63,7,61,42,21,9,123,24...] ,总数为N=100000,要求前M=5个最大的数

  1. 对10万个建立二叉堆,然后应用堆排序5次,即取出前5个最大(或最小)的数。
    只是一个可行的方法,在此不敖述,具体可参见《数据结构与算法分析:C语言描述》、《数据结构(C语言版)》严蔚敏等书中的堆排序。

  2. 考虑:能否维护一个数据结构用来存储排好序的5个数,要求如果输入数大于5个中最小的数,就将其插入至正确位置,并删除最小的数。这样对输入进行一次遍历,即可找出最大的5个数。
    此处想到的是用单链表,首先对输入中前5个数字升序排序,插入空的链表中。

//简单冒泡排序,输入少,对整体性能影响可忽略不计
for(int j=1; j<M; j++){
    for(int k=0; k<M-j; k++){
        if(input[k]>input[k+1]){
            tmp = input[k];
            input[k] = input[k+1];
            input[k+1]=tmp;
        }
    }
}
for(int i=0; i<M; i++){
    Insert(input[i],L,P);//依次插入链表
    P = P->Next;
}

图片 1.png

Position Tmp,TmpCell;
for( ; i<N; i++){ //对其余输入进行一次遍历
    P = Header(L); //表头
    do{
        Tmp = P;//暂存前驱元,保存位置
        P = P->Next;//第一个元素
        if( input[i] <= P->Value ){ //小于第一个元素或者后面的某一个元素
            if(P != L->Next){ //input[i]大小介于第一个元素与此位置的元素
                Insert(in[i],L1,Tmp); //插入
                TmpCell = L1->Next;
                L1->Next = TmpCell->Next;
                free( TmpCell ); //挤出第一个元素,也就是5+1=6个中最小的元素
            }
            break;
        }else if(input[i] > P->Value && IsLast( P, L )){ //如果大于最后一个(也就是最大的)元素
            Insert(in[i],L,P); //插入到最后
            TmpCell = L->Next; L->Next = TmpCell->Next; free(TmpCell); //删除第一个元素(6个中最小的)
            break;
        }
    } while( !IsLast(P, L) );
}

插入可能是这样的:
图片 2.png

删除首元可能是这样的:
图片 3.png

小结:当输入大数据量,而只需前m个最大(最小)值时,应用链表不失为一个好办法,它只对输入进行一次遍历,时间复杂度O(N),空间也只不过额外是一个含6个元素的链表大小而已。
欢迎指教。

USTB校园网自动登录chrome插件

请允许我自诩Geek一名,面对computer,连一秒都不愿等待,校园网登录页要等5s着实忍不了,所以抽空写了自动登录的插件。
先作声明:
1.此插件支持chrome和360极速浏览器
2.此插件只是本地存储,不会泄露您的密码,请放心使用
3.此插件不会影响您的ipv6地址

安装使用步骤:
1.浏览器地址栏输入 chrome://extensions/ 以打开扩展程序页面
2.下载附件autologin,将附件拖入页面以安装
2.png
3.刷新校园网登录页面,右上角LA(login automaticly)按钮处,输入自己的账号密码,点击“save”、“关闭中”以开启
3.png
4.OK了

由于chrome官方的安全策略,windows下的chrome可能会提示“已停用不支持的扩展”,解决办法详见chrome插件出故障的解决方案.rar

想说的话:
(校园网5s可能是官方折衷的做法,但牺牲每个同学每次登录的5s,并不是解决问题的好方式)
插件主要用了一晚上做出来,又一上午对细节调整完成,因为自己不愿等那5s,结果花了好几个小时。
登录校园网每个同学每次登录都要等上5s,所以希望我的几小时工作可以为大家省去无数个5s。

PHP的GD库freetype编译安装问题及解决办法

首先说明:

  1. 我的环境是Mac OS X,大家知道mac是基于unix,所以从源代码编译安装三步曲:./configure 、 make 、 make install 是都知晓的。
  2. 编译安装三步曲,是可以无限重复进行的。当然有时你要make clean一下。

在一些带验证码、做水印、一些图片处理的PHP项目中,要用到GD库(从PHP 4.3.0开始,绑定到PHP源代码的ext里),在./configure时,使用 --with-gd安装。通常GD库主要有libpng\libjpeg\freetype2\t1lib这四个依赖,而libpng需要zlib(unix/linux系统都会有这个库,不用自己下载安装)。
libpng\libjpeg\ freetype2 \t1lib 这四个库,就要自己先安装好
三种方式:

  1. 从他们的官网中下载稳定版,从源码安装,安装到/usr/local(./configure --prefix=/usr/local)
  2. yum apt-get等方式安装(我的mac没装这几个命令,所以我没有采用这种方式)
  3. 这个方式仅限Mac:Homebrew ———— The missing package manager for OS X,可去其github看文档,安装后几个简单命令,就把这些依赖的库安装好了,非常方便。

现在,我们已经解决了GD的所有依赖,只要在./configure时加上以下几个配置项:

--with-gd --with-zlib --with-png-dir=/usr/local --with-jpeg-dir=/usr/local --with-freetype-dir=/usr/local --with-t1lib=/usr/local 

再 sudo make 、 sudo make install 就完事了!
gd.png

遇到的一些问题:

  1. Mac是自带PHP的,版本稍旧一点且源码也没有了,不如把自带的干掉,重新编译安装一个(比如PHP 5.5)!系统默认的PHP安装在/usr,我们自己通常安装在/usr/local里。
    彻底删除自带的PHP:
cd /private/etc/
sudo rm -rf php-fpm.conf.default php.ini php.ini.default
cd /usr/bin/
sudo rm -rf php php-config phpdoc phpize
cd /usr/include
sudo rm -rf php
cd /usr/lib
sudo rm -rf php
cd /usr/sbin
sudo rm -rf php-fpm
cd /usr/share
sudo rm -rf php
cd /usr/share/man/man1
sudo rm -rf php-config.1 php.1 phpize.1
cd /usr/share/man/man8
sudo rm -rf php-fpm.8
  1. 因为不断试用关于GD的这几个配置项,我编译安装三步曲起码进行了十多次。你会发现第一次编译安装时间很长,之后重复编译安装很快就完成了,是因为有一些步骤就跳过了。所以改了一些配置项,phpinfo()中的预期的GD的信息却没有。所以重复三步曲前,make clean 进行一下就ok了。

我的配置项:./configure --prefix=/usr/local/php --enable-mbstring --with-apxs2=/usr/sbin/apxs --with-mysql --with-pdo-mysql --enable-zip --with-mysqli --with-curl=/usr/local/opt/curl
说明:
--with-apxs2=/usr/sbin/apxs 这个配置项是针对Apache2.x版本,=[DIR]为apxs命令的位置
--with-curl=/usr/local/opt/curl 详见curl安装

经典排序算法

  1. 冒泡
$arr = array(3,44,38,5,47,15,36,26,27,2,46,4,19,50,48);
$arr = array(2,3,4,5,15,19,26,27,36,38,44,46,47,48,50);
$times = 0;//记录比较的次数
$len=count($arr);
//控制需要冒泡的轮数
for($i=1;$i<$len;$i++){
  $swapped = false;//[优化]跳出循环之用
  //控制每轮 冒出一个数 需要比较的次数
  for($k=0;$k<$len-$i;$k++){
    $times += 1;
    if($arr[$k]>$arr[$k+1]){
      $tmp=$arr[$k+1];
      $arr[$k+1]=$arr[$k];
      $arr[$k]=$tmp;
      $swapped = true;
    }
  }
  $times += 1;
  if(!$swapped)
    break;
}
echo $times;print_r($arr);

冒泡排序无论代码怎么实现,交换的次数是同样。不同的是,设计良好的算法对于排序良好的输入,进行比较的次数会骤减。

  1. 插入排序
function insert_sort(&$arr){
    $sum = count($arr);
    for($i=1;$i<$sum;$i++){
        $tmp=$arr[$i];
        $key=$i-1;
        while($key>=0 && $tmp<$arr[$key]){
            $arr[$key+1]=$arr[$key];
            $key--;
        }
        $key != $i-1 && $arr[$key+1] = $tmp;//如果有移动,就插入
    }
}
  1. 希尔排序
function shell_sort(&$arr)
{
    if(!is_array($arr)) return;
    $n = count($arr);
    for($gap=floor($n/2);$gap>0;$gap=floor($gap/=2))
    {
        for($i=$gap;$i<$n;++$i)
        {
            for($j=$i-$gap;$j>=0 && $arr[$j+$gap]<$arr[$j];$j-=$gap)
            {
                $temp = $arr[$j];
                $arr[$j] = $arr[$j+$gap];
                $arr[$j+$gap] = $temp;
            }
        }
    }
}
  1. 选择排序
function selection_sort(&$array){
    $count=count($array);
    for($i=0;$i<$count-1;$i++){
        /*findtheminest*/
        $min=$i;
        echo'$min-->'.$array[$min].'-->';
        for($j=$i+1;$j<$count;$j++){
            //由小到大排列
            if($array[$min]>$array[$j]){
                //表明当前最小的还比当前的元素大
                $min=$j;
                //赋值新的最小的
            }
        }
        echo$array[$min].'coco<br/>';
        /*swap$array[$i]and$array[$min]即将当前内循环的最小元素放在$i位置上*/
        if($min!=$i){
            $temp=$array[$min];
            $array[$min]=$array[$i];
            $array[$i]=$temp;
        }
    }
}
  1. 快速排序
function quickSort($arr){
    if(count($arr)>1){
        $k=$arr[0];
        $x=array();
        $y=array();
        $size=count($arr);
        for($i=1;$i<$size;$i++){
            if($arr[$i]<=$k){
                $x[]=$arr[$i];
            }elseif($arr[$i]>$k){
                $y[]=$arr[$i];
            }
        }
        $x = quickSort($x);
        $y = quickSort($y);
        return array_merge($x,array($k),$y);
    }else{
        return $arr;
    }
}
  1. 选择排序
function selectSort(&$arr){
    $n = count($arr);
    for($i=0; $i<$n-1; $i++){
        $min_k = $i;
        $min_v = $arr[$i];
        for($j = $i+1; $j<$n; $j++){
            if($arr[$j] < $min_v){
                $min_v = $arr[$j];
                $min_k = $j;
            }
        }
        $arr[$min_k] = $arr[$i];
        $arr[$i] = $min_v;
    }
}