对比一下php和clojure

以前写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}

对比一下实现结果:

php中有eval,这函数可以把字符串当做php代码执行,如果在php中用上eval可能功能上会通用一些,那样就可以用"$1/$2"的形式,嗯,会大大简化代码量,不再需要switch了。虽然我没尝试过,但也知道eval的性能肯定是个问题。另外php的数学运算是中缀形式,而lisp是函数前缀,这样lisp的方式简单不会岐义,一个表达式支持灵活的参数,比如加法运算,如果用php的eval实现灵活的参数个数就需要多一层的抽象。

我对编程语言是有一种畏惧的心态,对任何编程语言都不能轻率地下结论,认识有限、知识无限。这里仅从我的认识简单对比一下实现结果,相信php有超出我知识范围的实现。