定义减少的arity部分函数

时间:2012-05-21 02:07:38

标签: clojure functional-programming lisp

我有时发现在Clojure中定义简化的arity版本的函数很方便,这些函数会返回一个部分函数,​​例如

(defn prefix 
  ([pre string] 
    (str pre ":" string))

  ([pre] 
    (fn [string]
      (prefix pre string)))) 

这意味着你可以这样做:

(prefix "foo" 78979)
=> "foo:78979"

((prefix "foo") 78979)
=> "foo:78979"

这似乎非常Haskell-ish并且避免了partial创建部分函数的需要。

但它在Lisp中被认为是良好的编码风格/ API设计吗?

3 个答案:

答案 0 :(得分:9)

使用partial创建一个curried函数是基于明确更好(在大多数情况下:)的概念。我发现这个概念在Clojure,Python等动态类型语言中更适用/使用可能是因为缺少类型签名/静态类型,使事情变得更明确。

答案 1 :(得分:7)

默认情况下,在函数调用的语言中,函数调用也是curry。当一个人写道 (f a b c),语言将其解释为(((f a) b) c)。在Clojure中并非如此。

我认为在默认情况下不调用调用的环境中创建curried函数会产生概念上的不匹配 - 这种结构可能会引起读者混淆(我的意思是代码的人类读者)。

如果你的函数有两个以上的参数,那么这个定义会很快变得难看。假设一个函数有4个参数。要完全模拟currying调用,当有人第一次传递2个参数然后剩下的两个参数时,你需要处理像((f a b) c d)这样的情况。在这种情况下,双参数函数的重载版本需要返回一个重载函数,该函数的行为会有所不同,具体取决于它是获得1还是2个参数。我想有可能用宏来自动化,但仍然。

此外,您无法定义默认参数和& rest构造。

答案 2 :(得分:6)

嗯...嗯......就个人而言,我宁愿看partial,因为这样可以清楚地知道发生了什么。

我不知道它是“好”还是“坏”的编码风格,但我以前从未在现有的Clojure代码中看过这种风格,我可以想象使用API​​的人会期望(prefix "foo" 78979)(prefix "foo")返回相同类型的对象。

为了明确两个功能之间的区别,你可以做一些类似的事情:

(defn prefix [pre string]
  (str pre ":" string))

(defn prefix-fn [pre]
  (fn [string]
    (prefix pre string)))

(prefix "foo" 78979)      ; => "foo:78979"
((prefix-fn "foo") 78979) ; => "foo:78979"