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


编译器异常:不支持从尾部位置递归

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

14 个答案

0

评论者:[email protected]

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

0

评论者:[email protected]

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

0
0

评论者:[email protected]

补丁被撤销,因为它在解构参数时会出现错误。

0

评论者:[email protected]

在修复一个补丁时,我遇到了一个相关问题::pre条件是否适用于每个recur“调用”。最初,我以为:pre条件只在最初的函数调用上检查一次,而在recur过程中永不检查。邮件列表中的人指出,recur从语义上看像是再次调用函数,所以:pre检查是合同的一部分。但是似乎没有人想要在每个递归中都进行:post检查,所以:post只会在最后发生。

这意味着自动在主体周围包裹一个循环(或嵌套fn*调用)不适用于:pre条件。修复将需要将:pre条件带入循环内部。

0
_注释来自: [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

注释来自: 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))
    acc))
0

注释来自: ambrosebs

补丁文件 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
已回答 by
...