如何在Clojure中测试高阶函数?

6

如何在 Clojure 中测试高阶函数?

我可以测试接受一个值的函数,并检查返回值是否符合预期。

那么如何对于高阶函数进行测试呢?

或者我们通常避免这样做吗?

2个回答

5
这是一个好问题。更准确地说,关于这个问题有两种情况:高阶函数“接受或产生其他函数作为参数或结果”。所以有两种情况:
1. 接受函数的函数。例如:`map`,`reduce`,`filter`。这些不太难测试;只需像普通函数一样提供所有参数即可。
2. 返回函数的函数。例如:`(fn [x] (fn [y] (+ x y)))`,`(partial filter #(> % 1))`。 这些很难测试,因为我们不能直接比较函数的相等性(搜索内涵和外延相等性进行全面讨论)。
很明显,简单地不测试不是一个很好的策略。为什么不采用 Haskell 的观点,即部分应用的函数本质上与返回函数的函数相同——换句话说,传递足够的参数给返回的函数,以获得可以测试相等性的结果。
只需小心注意测试中的耦合——确保您的测试用例实际上正在测试高阶函数的规范,而不仅仅是它返回的函数。

在这个问题上,宏定义也会被测试吗?特别是doXYZ? - Amogh Talpallikar

2

高阶函数仍然会返回结果,因此您仍然可以检查它。

例如,如果您想要测试 map 函数,请考虑它应该做什么:它应该接收一个函数和一个集合作为参数,将该函数应用于集合中的每个项,并返回结果的新集合。

因此,测试的一种简单方法是:

(is (= [1 2 3 4] (map inc [0 1 2 3]))

同样的原则也适用于测试另一个函数返回的高阶函数:在调用返回该高阶函数的函数之后,只需测试它是否返回了预期结果即可。
(defn adder [n]
  (fn [x]
    (+ x n)))

(is (= ((adder 10) 5) 15)) 

希望这能帮到您。

那真的很有道理。谢谢,我想我没有将函数视为值并且想到了其他东西。 只要没有变异,如果高阶函数为lambda和列表返回一致的值,则应与传递的任何lambda函数一致。非常感谢! - Amogh Talpallikar
但是对于返回闭包的函数呢? - Amogh Talpallikar
你能举个例子吗?我不明白为什么它会有任何不同的行为。 - leonardoborges
如果一个高阶函数返回一个函数,该函数基于4-5个不同的参数返回4-5个不同的函数。例如:(def algebraic-eq (gen-algebraic-func {:x [1,2,3] :y [2,3,4]}))gen-func返回一个函数,它是一个代数函数,接受一个变量字典作为键,并在这些变量的向量中将系数放在幂位置上,并返回一个函数,该函数接受:x,:y等的值并返回结果。那么我如何测试这样的函数?Matt Fenwick提到了这个问题。 - Amogh Talpallikar
1
这只是同一原则的延伸。无论您的函数生成函数返回多少个函数,只要您知道它们应用后应该做什么,使用上述方法测试它们就可以了。对于像您提到的纯函数,类似test.generative的东西也可以帮助,因为您只需要提供函数规范,框架就会确保它对于几百个值是正确的。 - leonardoborges

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接