2024 Clojure状态调查!分享您的想法。

欢迎!请参阅关于页面以了解有关该功能的更多信息。

0
编译器
Michael O'Keefe <[email protected]> 在邮件列表上发布了一个示例代码,仅在添加:post 条件时才会导致编译器错误。以下是我稍微修改过的版本


(defn g
  [xs acc]
  {:pre [(or (nil? xs) (sequential? xs))]
   :post [(number? %)]}
  (if (seq xs)
     (recur (next xs) (+ (first xs) acc))
     acc))


CompilerException java.lang_FRESupportedException: 只有从尾部位置可以递归

解决方案是将体包装在一个循环中,该循环简单地重新绑定原始参数。

14 个回答

0

评论者:[email protected]

宏展开表明,体被放置在 let 形式中以捕获后来用于后置条件测试的结果,但递归不再有适当的目标。一旦你理解了发生了什么,使用循环形式的解决方案就很容易,但这是一个意外的限制。

0

评论者:[email protected]

在体周围使用局部 fn* 并用原始参数调用它,以便递归有适当的目标。更新:对于处理解构来说还不够好。补丁已撤回。

0
0

评论者:[email protected]

由于它破坏了解构参数,因此撤回了补丁。

0

评论者:[email protected]

在修改补丁时,我遇到了一个相关问题::pre 条件是否应用于每个 recur “调用”?最初,我以为 :pre 条件应该在初始函数调用时检查一次,并在 recur 期间不检查。邮件列表上的人指出,recur 在语义上类似于再次调用函数,所以 :pre 检查是协议的一部分。但似乎没有人希望对每个递归执行 :post 检查,所以 :post 只会在结束时发生。

这意味着自动在体周围包裹循环(或嵌套 fn* 调用)不会适用于 :pre 条件。修补程序必须将 :pre 条件放在循环内部。

0
_评论者:[email protected]_

我放弃了这个错误。我的方法过于复杂,以处理一个边缘情况。我建议任何遇到这个问题的人都使用“loop”绕道。


定义一个函数:defn g2
  [xs acc]
  {:pre [(or (nil? xs) (sequential? xs))]
   :post [(number? %)]}
  (loop [xs xs acc acc]
    (if (seq xs)
       (recur (next xs) (+ (first xs) acc))
       acc)))

0

评论者:ambrosebs

添加一个处理剩余参数和解构的补丁。

0
_评论者:[email protected]_

关于Steve对:pre的解释问题,我认为g应像以下g3例子那样工作,它使用了显式递归(确实可行,每次调用时都看起来会检查:pre条件,并在一次调用中检查:post条件)


(defn g3
  [xs acc]
  {:pre [(or (sequential? xs) (nil? xs)) (number? acc)]}
   :post [(number? %)]}
  (if (seq xs)
    (g3 (next xs) (+ (first xs) acc)))
  )
0

评论者:ambrosebs

patch clj-1475.diff 处理了解构、前置条件和剩余参数

0

评论者:[email protected]

我认为clj-1475.diff补丁看起来很好。

0

评论者:alexmiller

请不要用“patch”作为标签,那是补丁字段的目的。有关良好和不良标签的列表,请访问http://dev.clojure.org/display/community/Creating Tickets

0

评论者:[email protected]

更有经验的评论者可以看看CLJ-701,以防万一它适用于提出的补丁。

0

评论者:hiredman

re clj-701

在jvm字节码中表达循环表达式语义比较棘手,因此编译器有点推卸责任,将表达式循环提升到立即调用的匿名函数中,捕获任何在循环中需要的变量,这有一些问题,比如CLJ-701中看到的问题,丢失类型数据——clojure编译器在函数间不跟踪类型数据,额外的函数对象分配(jit可能会很好地处理这个问题,我不确定)等。

当clj-701与这张票的冲突世界交汇时,这张票上修补的部分将函数体提取出来作为循环表达式,如果没有在clj-701中修补,将会出现我上面列举的问题,但我们已经在某些地方遇到了这些问题,这些地方使用作为表达式的难以用字节码表达的内容(尝试和循环)作为表达式,可能这无关紧要,或者可能clj-701将以某种方式修复以减轻这些问题。

一般思考

人们喜欢断言的一个功能似乎是能够在生产环境中禁用它们(我从未真正看到有人用clojure这样做),assert和:pre/:post有一些能力做这件事(可能只适用于宏观扩展时间,我不记得了),因为循环提升可能会影响性能,可能有一种机制来禁用它(可能使用与assert相同的标志?)。

0
...