【问题标题】:Implement range function in clojure return error在clojure返回错误中实现范围函数
【发布时间】:2018-03-06 01:09:15
【问题描述】:

我正在尝试在 clojure 中实现 range 函数,但我的实现返回了一个我无法理解的错误。这里是:

(defn implement-range [a b] (
  if (= a b)
  (conj nil b)
  ((conj nil a) (implement-range (inc a) b))))

我正在尝试以递归方式进行操作,但它还没有完成,因为我遇到了这个错误:

ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IFn  user/implement-range

我认为这是我看不到的括号问题(我是 clojure 的新手 :))。有什么想法吗?

提前感谢您的帮助

编辑:

如果我这样调用函数,我想返回类似 (1 2 3 4) 的内容:

(implement-range 1 5)

【问题讨论】:

  • 你想用((conj nil a) (implement-range (inc a) b))做什么?
  • 我正在尝试以递归方式将元素添加到列表中。但是当我研究时,我将其更改为列表,而不是 conj
  • 使用((conj nil a) (implement-range (inc a) b)))),您尝试将(conj nil a) 作为函数调用,(implement-range (inc a) b) 作为参数。这是你的意图吗?
  • 哈哈,没有。我现在看到这根本没有意义。我将发布我的部分解决方案作为答案,以便您查看是否有意义,以及改进它的可能方法

标签: clojure functional-programming


【解决方案1】:

你很亲密:

(defn implement-range [a b]
  (if (>= a b)
      '()
      (conj (implement-range (inc a) b) a)))

(implement-range 0 5)
=> (0 1 2 3 4)

首先,主要问题是((conj nil a) (implement-range (inc a) b)))) 试图将(conj nil a) 作为函数调用,而(implement-range (inc a) b) 作为参数。我不完全确定您要在这里做什么,但这绝对不是您想要的。

我做了三处改动:

  • 你需要conj当前号码到递归调用的结果。这是“不安全”的递归,因为递归调用不在尾部位置,但是在搞砸的时候,这没什么大不了的。但是,如果您打算真正使用它,您将需要使用另一种方法。

  • 如果你输入一个大于ab,它就会爆炸。我通过更改基本情况条件解决了这个问题。

  • 您的基本情况没有任何意义。递归停止后,您需要添加到一个空列表中。

只是一个旁注,任何时候你看到((,小警钟都应该响起。这通常是一个问题的征兆,除非你喜欢写((comp str inc) 1)之类的东西;其中表达式中的第一个形式计算为一个函数。

【讨论】:

  • 感谢您的回答和提示。我将基本返回值更改为 nil,然后删除了 nil 值。我知道这很丑,但正如我所说,我仍然是 clojure 的新手。你的答案比我的更容易阅读:)
  • @GabrielMesquita 真的,你应该使用一个好的 IDE 来编写。 Clojure 是一门很棒的语言,但是除非您有一些东西可以帮助您使用大括号,否则很难轻松编写。我使用 IntelliJ+Cursive,它是免费的,而且是一个很棒的 IDE。函数定义第一行的那个奇怪的左大括号甚至无法使用 parinfer。
  • parinfer 让您可以使用缩进来编写 Clojure,就像您在编写 Python 一样。它会根据缩进为您处理大括号。
  • 太棒了,我在 3 天前开始学习 clojure,哈哈,我正在使用 atom 和 proto REPL。我会试试 intellij :)
  • 这是一个巨大的 IDE,所以需要一些时间来适应。不过我已经使用了 2 年了,我会说它绝对值得花时间学习。
【解决方案2】:

最简单(也是最经典的)应该是这样的:

(defn implement-range [a b]
  (when (< a b)
    (cons a (implement-range (inc a) b))))

user> (implement-range 1 20)
;;=> (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

【讨论】:

  • 也懒惰,当包裹在 lazy-seq 之前 when 之前
【解决方案3】:

我做了一些研究,发现以这种方式使用 conj 没有任何意义。所以我想出了这个:

(defn implement-range [a b] (
  if (= a b)
  b
  (flatten (list a (implement-range (inc a) b)))))

所以如果我用 1 4 个参数调用函数,结果将是:

(1 2 3 4)

但是,由于我正在尝试实现诸如范围功能之类的功能,因此还需要删除最后一个元素。

编辑 1

我写的最后一个函数是这样的:

(defn implement-range [a b] (
  if (= a b)
  nil
  (remove nil? (flatten (list a (implement-range (inc a) b))))))

编辑 2 经过更多研究后,我找到了另一种解决此问题的方法,代码行数更少:

(defn new-range [a b]
  (take (- b a) (iterate inc a)))

【讨论】:

  • 我不会使用 flatten。这是构建嵌套结构的一种非常迂回的方式,只是为了解除嵌套!使用consconj 更简单、更自然。
  • 是的,上面的答案更具可读性和惯用性。这个答案有效,但远非理想的实现方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
  • 2014-05-17
  • 2019-11-11
  • 1970-01-01
  • 2011-07-06
相关资源
最近更新 更多