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.langUnsupportedOperationException:只能从尾部位置递归

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

14 个答案

0

评论者:[email protected]

宏展开显示,主体被放置在let形式中,以捕获用于以后与:post条件一起测试的结果,但递归不再有适当的目标。一旦您了解了发生了什么,使用循环形式的解决方案就很容易,但这是一种令人惊讶的限制。

0

评论者:[email protected]

在主体周围使用本地fn*,并用原始参数调用它,以便递归具有适当的目标。更新:不足以处理解构。补丁撤回。

0

评论者:[email protected]

原始主题讨论链接:[https://groups.google.com/d/topic/clojure/Wb1Nub6wVUw/discussion](https://groups.google.com/d/topic/clojure/Wb1Nub6wVUw/discussion "https://groups.google.com/d/topic/clojure/Wb1Nub6wVUw/discussion")

0
by

评论者:[email protected]

因在解构参数上出错,撤回补丁。

0
by

评论者:[email protected]

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

这意味着自动围绕主体包裹循环(或嵌套fn*调用)对于:pre条件是不行的。修复必须将:pre条件引入循环内部。

0
by
_评论者:[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
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
by

评论者:ambrosebs

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

0
by

评论者:[email protected]

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

0
by

由:alexmiller发表的评论

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

0
by

评论者:[email protected]

更有知识的评论者可能需要看看CLJ-701,以防情况适用。

0
by

由:hiredman发表的评论

re clj-701

在jvm字节码中表达循环表达式语义是很棘手的,所以编译器会略微避开,将表达式循环提升到立即调用的匿名函数中,覆盖循环所需的任何作用域,这会带来一些问题,就像在CLJ-701中看到的那样,失去了Clojure编译器在函数间不跟踪的类型数据,额外的函数对象分配(jit可能处理得相当好,我无法确定)等等。

clj-701和此票证相交的世界是此票证的补丁将函数体提升为循环表达式,如果没有clj-701中的补丁,将会有我列出的问题,但是我们已经在任何用作表达式(尝试并循环)的难以用字节码表达的事物中有了这些问题,可能没关系,或者clj-701将通过某种方式修复以缓解这些问题。

一般思考

人们似乎喜欢断言的一个功能是能够在生产中禁用它们(我从未见过有人用Clojure这样做过),assert和:pre/:post有一些能力来实现这一点(这可能只在工作宏展开时工作,我不记得了),因为循环的提升可能会影响性能,可能需要一个机制来禁用它(也许可以使用断言使用的同一个标志?)。

0
...