November 7, 2019
By: Kevin
函数编程的一个小例子
问题
下面的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))))