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))


编译器异常:java.lang.UnsupportedOperationException:只能从尾递归位置递归

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

14 答案

0 投票

评论由:[email protected] 撰写

宏展开显示,主体被放置在 let 形式中以捕获结果,以便稍后与 post 条件一起进行测试,但递归不再有适当的目标。一旦您了解了情况,使用循环形式的解决方案就非常简单,但这仍然是一个意外的限制。

0 投票

评论由:[email protected] 撰写

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

0 投票
0 投票

评论由:[email protected] 撰写

补丁撤回,因为它在结构化参数上会崩溃。

0 投票

评论由:[email protected] 撰写

在处理补丁时,我遇到了一个相关的问题::pre 条件是否应用于每个 recur "调用"。最初,我认为 :pre 条件仅在初始函数调用时检查一次,而在 recur 期间不会检查。邮件列表上的用户指出,recur 在语义上相当于再次调用函数,因此 :pre 检查是协议的一部分。但似乎没有人希望对每次递归都进行检查,所以 :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 投票
by
评论由:[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))
    acc)
0 投票
by

评论人:ambrosebs

clj-1475.diff 补丁处理了解构、先决条件和剩余参数

0 投票
by

评论由:[email protected] 撰写

我认为clj-1475.diff补丁看起来不错。

0 投票
by

评论由:alexmiller 提出

请勿使用“patch”作为标签,这是补丁字段的用途。有关良好和不良标签的列表,请参阅 http://dev.clojure.org/display/community/Creating Tickets

0 投票
by

评论由:[email protected] 撰写

理论知识更加丰富的评论者可能需要查看CLJ-701,以防这个补丁与此有关。

0 投票
by

评论由:hiredman 提出

re clj-701

在jvm字节码中表达循环表达式语义很复杂,因此编译器有点规避,将表达式循环提升到匿名函数中,立即调用,关闭任何在范围内需要循环的所有内容,这有一些问题,如CLJ-701所见,丢失类型数据,Clojure编译器不跟踪函数之间的类型数据,额外的函数对象分配等。

当clj-701和这里的票据在某个接合点上碰撞时,这个票据上的接合处会将函数体提升为一个循环表达式,如果没有在clj-701中补丁,将会出现我上面列出的问题。但我们现在遇到的问题是我们任何地方都难以在字节码中用表达式(尝试和循环)表示,也许这无关紧要,或者clj-701可能会以某种方式得到修复以减轻这些问题。

一些随想

人们似乎喜欢断言的一个特性是它们可以在生产环境中禁用(我从未在Clojure中见过有人这样做),assert和:pre/:post有一些这样的能力(可能仅在宏展开时起作用,我不记得了),由于循环提升可能影响性能,拥有某种禁用机制可能很好(也许使用断言的相同标志?)。

0 投票
...