有条件的"赋值"在函数式编程中

时间:2017-01-10 13:12:39

标签: clojure functional-programming lisp

我正在编写一些没有副作用的东西,但我的代码不是很易读。 请考虑以下代码:

(let [csv_data (if header_row (cons header_row data_rows) data_rows)]
)

我试图在代码块中使用csv_data。在header_row的存在下,有什么干净的条件?我看过if-let,但看不出这对我们有什么帮助。

我遇到了类似于函数for循环的情况,我将结果绑定到局部变量,代码看起来像一堆表达式。

在很多情况下,我真的必须创建一个单独的辅助函数吗? 我在这里缺少什么?

4 个答案:

答案 0 :(得分:6)

使用cond->>

(let [csv_data (cond->> data_rows
                 header_row (cons header-row)]
   )

它的作用类似于常规的->>宏,但在每个线程表之前,必须放置一个测试表达式来确定是否使用线程表。

还有cond->。在此处阅读有关线程宏的更多信息:Official threading macros guide

答案 1 :(得分:2)

首先,不要使用下划线,更喜欢破折号。

其次,辅助函数没有任何问题;毕竟,这似乎是处理您的特定数据格式的必要条件。

第三,如果您可以更改数据以便可以跳过这些决策并对所有极端情况进行统一表示,那就更好了。标题行包含不同类型的数据(列名?),因此您可能希望将它们分开:

(let [csv {:header header :rows rows}]
  ...)

或许在某些时候你可以拥有"标题"和"行"属于同一类型:行序列。然后你可以直接连接它们。 ensure-x成语是规范化数据的常用方法:

(defn ensure-list [data]
  (and data (list data)))

例如:

user=> (ensure-list "something")
("something")
user=> (ensure-list ())
(())
user=> (ensure-list nil)
nil

因此:

(let [csv (concat (ensure-list header) rows)]
  ...)

答案 2 :(得分:1)

我会提出一个实用工具宏。像这样:

(defmacro update-when [check val-to-update f & params]
  `(if-let [x# ~check]
     (~f x# ~val-to-update ~@params)
     ~val-to-update))

user> (let [header-row :header
            data-rows [:data1 :data2]]
        (let [csv-data (update-when header-row data-rows cons)]
          csv-data))
;;=> (:header :data1 :data2)

user> (let [header-row nil
            data-rows [:data1 :data2]]
        (let [csv-data (update-when header-row data-rows cons)]
          csv-data))
;;=> [:data1 :data2]

这是非常普遍的,让你完成更复杂的任务,然后只需简单的工作。例如,如果检查是真的,你想要反转一些coll,并连接另一个列表......

user> (let [header-row :header
            data-rows [:data1 :data2]]
        (let [csv-data (update-when header-row data-rows
                                    (fn [h d & params] (apply concat (reverse d) params))
                                    [1 2 3] ['a 'b 'c])]
          csv-data))

;;=> (:data2 :data1 1 2 3 a b c)

<强>更新 正如@amalloy所注意到的,这个宏应该是一个函数:

(defn update-when [check val-to-update f & params]
  (if check
     (apply f check val-to-update params)
     val-to-update))

答案 3 :(得分:-1)

在考虑命名空间中单行辅助函数的“成本”后,我想出了一个本地函数:

(let [merge_header_fn   (fn [header_row data_rows] 
                          (if header_row 
                            (cons header_row data_rows) 
                            data_rows))
      csv_data (merge_header_fn header_row data_rows) ]
  ...
  <use csv_data>
  ...
) 

除非有人能提出更优雅的处理方法,否则我会将此作为答案。