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 形式中以捕获结果,以便稍后与后置条件一起进行测试,但 recur 已不再具有适当的目标。如果您理解正在发生的事情,那么使用循环形式的解决方案很容易,但这是一个意外的限制。

0

由:[email protected] 评论

在主体周围使用局部 fn* 并用原始参数调用它,以便 recur 有适当的目标。更新:对于处理解构不可行。撤销补丁。

0
0
by

由:[email protected] 评论

因为会在解构参数上失败,所以撤回了补丁。

0
by

由:[email protected] 评论

在处理一个补丁的时候,我遇到了一个相关的问题::pre条件是否应该应用于每一个recur "调用"。最初,我认为:pre条件应该只在最初的函数调用时检查一次,而在recur过程中不应该再检查。邮件列表中的人指出,recur在语义上类似于再次调用函数,所以:pre检查是合同的一部分。但是没有人想要在每一次递归中做:post检查,所以:post只有在最后才会发生。

这意味着自动将循环(或嵌套fn*调用)包装在主体周围对于:pre条件是不起作用的。修复必须将:pre条件带入循环内部。

0
by
评论者:[email protected]

我要放弃这个虫眼的处理了。我的方法给处理边界情况增加了太多的复杂性。我建议任何遇到这个问题的使用者尝试"循环"的解决方案。


(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
by

评论者: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

评论者: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
通过
...