如何以高效和干燥的方式验证函数参数?

时间:2013-02-04 12:06:52

标签: clojure

假设我有三个函数在矩阵上运算:

(defn flip [matrix] (...))
(defn rotate [matrix] (...))
(defn inc-all [matrix] (...))

想象一下,每个函数都需要一个向量向量的向量(其中每个内向量的长度相同)才能正常运行。

我可以提供一个断言矩阵函数来验证矩阵数据的格式是否正确:

(defn assert-matrix [matrix] (...) )

然而,翻转函数(例如)无法知道数据是否已传递给函数已被验证(完全取决于用户是否可以在将其传递给函数之前对其进行验证)。因此,为了保证正确性,需要将翻译定义为:

(defn flip [matrix]
    (assert-matrix matrix)
    (...))

这里有两个主要问题:

  • 每次调用矩阵函数时都必须继续调用assert-matrix。
  • 每当我创建矩阵函数时,我都要记得调用assert-matrix。有可能我会忘记因为重复这个很乏味。

在面向对象语言中,我创建了一个名为Matrix的类,其中包含一个构造函数,用于在创建实例时检查构造函数args的有效性。没有必要重新检查有效性的方法,因为他们可以确信在初始化类时数据已经过验证。

如何在Clojure中实现这一目标?

2 个答案:

答案 0 :(得分:2)

有几种方法可以只验证一次数据结构,例如,您可以按照以下方式编写with-matrix宏:

(defmacro -m> [matrix & forms]
  `(do
    (assert-matrix ~matrix
      (-> ~matrix
        ~@forms))

允许你这样做:

(-m> matrix flip rotate)

以上扩展了线程宏以更好地处理您的用例。

同一种方法可能存在无限的变化,但这个想法应该仍然是相同的:宏将确保只有在验证成功时才执行一段代码,函数在矩阵上运行而没有任何嵌入的验证代码。而不是每个方法执行一次,每个代码块将执行一次验证。

另一种方法是确保矩阵函数的所有代码路径都在某处具有验证边界。

您可能还想查看trammel

答案 1 :(得分:2)

您可以使用协议来表示矩阵上的所有操作,然后创建一个类似于"构造函数的函数"对于矩阵:

(defprotocol IMatrix                                                                                                                                 
  (m-flip [_])                                                                                                                                       
  (m-rotate [_])                                                                                                                                     
  (m-vals [_]))                                                                                                                                      

(defn create-matrix [& rows]                                                                                                                         
  (if (apply distinct? (map count rows))                                                                                                             
    (throw (Exception. "Voila, what are you doing man"))                                                                                             
    (reify                                                                                                                                           
      IMatrix                                                                                                                                        
      (m-flip [_] (create-matrix rows))                                                                                                              
      (m-rotate [_] (create-matrix rows))                                                                                                            
      (m-vals [_] (vec rows))))) 



(def m (create-matrix [1 2 3] [4 5 6]))                                                                                                              
(m-flip m)