【问题标题】:loop / return in Clojure. What is 'tail' position?Clojure 中的循环/返回。什么是“尾巴”位置?
【发布时间】:2021-02-08 16:31:35
【问题描述】:

我只是在 REPL 中构建一个函数,然后遇到了这个问题。
我定义了一个符号 S 并给它一个值:

(def S '(FRUIT COLORS (YELLOW GREEN) SKIN (EDIBLE INEDIBLE)))

最终,我想要一个函数,它采用参数列表中的第一个条目以及任何和所有后续参数对并将它们应用于第一个条目。我的编码从来没有那么远。我想使用循环/递归构造(应该我?),这是我在 REPL 中的进展:

(loop [KV# (rest S)]
    (if (empty? KV#)
        nil
        (
            (pprint S, (first KV#), (second KV#))
            (recur (rest (rest KV#)))
        )
    )
)

我收到“只能从尾部位置重复”编译器错误。
在查看了这方面的所有内容(包括 Stack Overflow 中的 7 或 8 篇文章)之后,我只能问:嗯?!
我是新来的。如果 recur 不在尾部位置,有人可以向我解释为什么吗?
与“if”语句语法有关吗?啊! Clojure 不适合弱者!谢谢。

【问题讨论】:

  • 我的 Clojure 生锈了,但这段代码看起来会在运行时失败。您需要在 () 中使用 do 来包装对 pprintrecur ((do (pprint S, (first KV#), (second KV#)) (recur (rest (rest KV#)))) 的调用,否则您将尝试将 pprint 的返回视为可调用。这可能会修复问题也是。目前,recur 是函数的参数,这意味着它不会是函数中执行的最后一件事(使用它的值的函数将是)。
  • 请查看此文档列表:github.com/io-tupelo/clj-template#documentation 可能从“Brave Clojure”和“Getting Clojure”开始。这个问题对于 StackOverflow 来说实在是太笼统了。
  • 以更熟悉的方式考虑以下内容:factorial(n) { if (n == 1) { return 1; } return n * factorial(n - 1); }。尾部位置的意思是“完成的最后一件事”,递归调用是函数体中的最后一个元素,在尾部位置吗?不!最后完成的是n 的乘法和递归调用。如果它是像阶乘示例或聚合 return [a, b, someRecursiveCall(c)]; 这样的表达式的一部分,则它不是尾调用。目前尚不清楚您要在通话中做什么,但 recur 显然嵌套在其他内容中。
  • @AlanThompson:这可以被引用为 this questionthis question 的重复,但因为它实际上不是尾部位置问题,而是一个轻微但常见的语法错误,我发布了一个回答。
  • 独立于尾调用问题:大多数情况下,您可以通过使用 coljure 的序列操作函数来避免循环/重复。(doseq [[k v] (partition 2 (rest S))] (println S k v)) 会做同样的事情,同时保留代码简洁且不易出错。

标签: intellij-idea clojure cursive


【解决方案1】:

您在 Clojure 中犯了我最喜欢的错误之一 - 您尝试使用括号对代码进行分组。您需要使用(do ...) 表单将表单组合在一起,如下所示:

(loop [KV# (rest S)]
    (if (empty? KV#)
        nil  ; then
        (do  ; else
          (pprint S, (first KV#), (second KV#))
          (recur (rest (rest KV#)))
        )
    )
)

这消除了“重复不在尾部位置”的问题,但 仍然 失败 - pprint 上的一个 arity 异常 - 但我会留给你解决。

我是如何发现这一点的?我的规则是,每当我发现两个左括号在一起时,我会立即假设我犯了一个错误,我需要弄清楚我做错了什么。在这种情况下,它有点难以发现,因为左括号被中间的空格隔开——但从词汇扫描仪的角度来看,它们仍然是相邻的。所以你只需要学会像词汇扫描仪一样思考。 :-)

【讨论】:

  • 鲍勃·J:有效!谢谢你,特别是“2个左括号”警告。
  • 致癌物:你一出手就有了。
  • leetwinski:我喜欢它!分割!我想我的 Clojure 里程表上没有足够的里程来拥有我词汇表中的每一个内在功能。我想知道 Clojure 中是否有这样的功能。科尔!
  • @LionelGoulet,你知道,Bob Jarvis 的两个左括号规则是一个很好的经验法则,但是有 个适当的用法,其中两个左括号出现.但是,当您第一次学习 Clojure 时,它​​们不太可能出现(除非您正在学习一个非常介绍一些稍微更深层次的函数式编程思想的教程(例如 The Little Schemer,for Scheme))早期的)。我还建议学习正确的 Lisp/Clojure 代码格式,并使用知道如何格式化 Clojure 的编辑器。一旦你习惯了它,它可以帮助你发现某些错误。
猜你喜欢
  • 1970-01-01
  • 2016-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多