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.UnsupportedOperationException: 只能从末尾位置开始递归

解决方案是将主体包装在循环中,该循环仅重新绑定原始参数。

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] 发布_

我放弃了这个bug。我的方法给处理边界情况增加了太多复杂性。我建议需要处理此问题的任何人使用"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 发布

添加处理rest参数和解构的补丁

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))
    acc))
0
by

评论由:ambrosebs 发布

patch clj-1475.diff 处理了解构、前条件和非严格参数

0
by

评论由:[email protected]发表

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

0
by

评论者为:alexmiller

请勿使用"patch"作为标签,标签的用途是"Patch"字段。有关良好和坏的标签列表,请参阅http://dev.clojure.org/display/community/Creating+Tickets

0
by

评论由:[email protected]发表

可能更了解情况的其他评论者可以查看CLJ-701,以防它适用于建议的补丁。

0
by

评论者为:hiredman

关于clj-701的回复

在jvm字节码中表达循环表达式语义是有些技巧的,所以编译器有点回避,将表达式循环提升为立即调用的匿名函数,关闭任何在作用域中需要循环的东西,这有一些问题,比如CLJ-701中看到的问题,丢失类型数据,Clojure编译器不会跨函数跟踪,额外的函数对象分配(jit可能处理得相当好,我不确定)等。

clj-701的世界和这个票据相冲突在于这个票据的补丁将函数体提升为循环表达式,没有clj-701中的补丁,将会有我列出的问题,但我们已经在任何难以用表达式表达的字节码(例如循环)作为表达式的情况下有这些问题,可能没关系,或者clj-701将以某种方式修复以减轻这些问题。

一般思考

似乎人们喜欢断言中的一个特性是能够禁用在生产环境中的使用(我实际上从未见过有人用Clojure这样做过),assert和:pre/:post有一定的能力实现这个功能(可能只会在宏展开时生效,我不记得了),由于循环提升可能会影响性能,因此有一种机制可以禁用这个功能可能是个不错的想法(也许可以使用与assert相同的标志?)。

0
...