以前写python较多,突然又要写php,看自己的代码发现我对程序的实现有了些改变,最明显的一点就是数据和逻辑在代码中分开。以前用if else多,现在用foreach多。
以前有一个php项目,对一些数据衍生计算出一些新的指标,比如有pv click,现在要计算“点击率”。实现是通过一个array做为数据,用foreach做计算,发现“除”不好设计,当时是所有衍生指标都是除法,也就没深究,在foreach时都用“除”就行了。
当前又一个php项目,类似的问题,为了代码的可读性希望能在数据中包含有数学计算的动作,不是只有“除法”。 用php实现了一个看上去很笨的代码:
<?php
$exe=array(
"a"=>array('/','click','pv'),
"b"=>array('*','click','pri'),
"c"=>array('-','pv','uv'),
"d"=>array('+','uv','pri'),
);
$arr=array(
"pv"=>123,
"click"=>23,
"pri"=>3.2,
"uv"=>100,
);
foreach($exe as $k=>$v){
$v1=$arr[$v[1]];
$v2=$arr[$v[2]];
switch($v[0]){
case '/':
$arr[$k]=$v1/$v2;
break;
case '*':
$arr[$k]=$v1*$v2;
break;
case '-':
$arr[$k]=$v1-$v2;
break;
case '+':
$arr[$k]=$v1+$v2;
break;
}
}
print_r($arr);
结果数据是:
Array
(
[pv] => 123
[click] => 23
[pri] => 3.2
[uv] => 100
[a] => 0.1869918699187
[b] => 73.6
[c] => 23
[d] => 103.2
)
这个能满足项目需求,但实现的缺少美感,很生硬。最近在学clojure,于是用clojure试着实现一下。
(def exe {:a [/ :click :pv]
:b [* :click :pri]
:c [- :pv :uv]
:d [* :uv :pri]})
(def dict {:pv 123
:click 23
:pri 3.2
:uv 100})
(println (into dict
(map (fn [p]
(let [[k [e-fn & e-keys]] p]
{k (apply e-fn (map dict e-keys))}))
exe)))
结果:
{:a 23/123, :c 23, :click 23, :b 73.60000000000001, :d 320.0, :pv 123, :uv 100, :pri 3.2}
对比一下实现结果:
- clojure的实现更强大一些,扩展起来更简单,支持“加减乘除”之外的任意的计算方式,只需要在exe数据中增加相关内容就行,而且参与计算的数据个数也不局限在两个,可以增加多个数据。
- clojure结果的":a"显示"23/123",惰性求值,这样可以保持数据继续计算后的精度。
- clojure结果中的":b",是float类型,有精度损失。
- php的代码容易上手,直接告诉解析器怎么做,而clojure的抽象程度高一些,初次接触不容易理解(我也是试了多次)。
php中有eval,这函数可以把字符串当做php代码执行,如果在php中用上eval可能功能上会通用一些,那样就可以用"$1/$2"的形式,嗯,会大大简化代码量,不再需要switch了。虽然我没尝试过,但也知道eval的性能肯定是个问题。另外php的数学运算是中缀形式,而lisp是函数前缀,这样lisp的方式简单不会岐义,一个表达式支持灵活的参数,比如加法运算,如果用php的eval实现灵活的参数个数就需要多一层的抽象。
我对编程语言是有一种畏惧的心态,对任何编程语言都不能轻率地下结论,认识有限、知识无限。这里仅从我的认识简单对比一下实现结果,相信php有超出我知识范围的实现。