November 7, 2019
By: Kevin

函数编程的一个小例子

  1. 问题
  2. 思考
  3. 实现
  4. 补充

问题

下面的map, 只保留每个key的数组中, 对应的:required下的数组为true的元素:

(def x {:title ["名称" "编码" "排序" "最小值" "最大值" "图片"]
        :show-in-list [true true true true false false]
        :key [:part_name :part_code :show_order :min_size :max_size :part_image]
        :input-type [:txt :txt :num :num :num :img]
        :value [nil nil nil nil nil nil]
        :required [true true false false false false]})

即处理成以下结果

{:title ["名称" "编码"],
 :show-in-list [true true],
 :key [:part_name :part_code],
 :input-type [:txt :txt],
 :value [nil nil],
 :required [true true]}

思考

  • :required这个key对应的 [true true false false false false] 为过滤条件, 将false位置的元素移除. 这本身是一系列的动作 (actions).
  • 一些列的动作 (actions) 是由单个动作 (action) 组合 (compose) 而成
  • 单个的动作就是移除特定位置的元素
action移除特定位置的元素, actions就是这一系列函数以特定顺序的组合. 这个组合可以应用于其他key对应的vector.
函数--------------------> 高阶函数-------------------------------> apply 高阶函数到其他vector

考虑使用reduce-kv函数, 思路很清晰, 就差个函数actions了:

(reduce-kv
 (fn [m k v] (assoc m k (actions v)))
 {}
 x)

问题是怎么定义这一些列的动作(actions).

实现

action能够从s中移除下标为i的元素

(defn action [i s]
  (vec (concat (drop-last (- (count s ) i)  s) (drop  (inc i) s ))))

一些列的动作actions, 根据:required对应数组的属性决定. 注意:actions返回值是函数

(def actions
  (->> x
       :required
       (map-indexed (fn [i e]
                      (if-not e
                        (partial action i) ;; 这里才是真正的action,知道自己要移除哪个元素i
                        identity)))        ;; 需要返回一个函数, 保留原来元素 
       (apply comp)))

将这一些列的动作应用于每一个key下面的数组

(reduce-kv
 (fn [m k v] (assoc m k (actions v)))
 {}
 x)

大功告成! 这个例子比较综合:

  • 高阶函数comp, 可以对函数进行组合, 得到新的函数.
  • 闭包(partial action i) 捕获了当前元素的下标
  • identity函数
  • apply

函数式编程是描述性的, 而不是命令式的, 没有使用任何的变量, 循环, 也没有副作用; 想清楚写出来,很难出错.

补充

用map也很容易实现, 因为map后面可以跟多个collection.

(defn get-index [lst v]
  (->> lst
       (keep-indexed (fn [i e] (when (= v e ) i)))
       (some identity)))

(def index (get-index (keys x) :required))

(zipmap (keys x)
        (->> (vals x)
             (apply map (fn [& args]
                          (when (nth args index) args)))
             (remove nil?)
             (apply map (fn [& args] args))))
Tags: clojure 函数式编程 clojurescript