【问题标题】:Recur not at tail position不在尾部位置重复出现
【发布时间】:2016-08-25 01:36:22
【问题描述】:

我怎样才能使用类似于recurnot at tail 位置的东西?

看看我的代码:

(defn -main [& args]

  (println "Hi! Type a file name...")

  (defn readFile[])
    (let [fileName(read-line)]
    (let [rdr (reader fileName)]
      (if-not (.exists rdr) 
        ((println "Sorry, this file doesn't exists. Type a valid file name...")
         (recur)))
         (defn list '())
         (doseq [line (line-seq rdr)]
           (if-not (= "" line)
             (concat list '(line)))
             (list))))

(defn fileLinesList (readFile))
  ...
  ...)

我知道我不能在这里使用recur...但我也不知道如何在 clojure 中使用它。

我是 Clojure 的新手,我来自 OOP 上下文。所以……

有没有办法在这种情况下使用递归? 有什么替代方案?

【问题讨论】:

  • 您的目标是编写一个从文件中读取文本行的函数还是现有函数对您来说足够好?
  • 虽然你没有这样写,但你的函数逻辑似乎是'如果 然后 否则 ',在这种情况下函数尾递归的。所以我建议像这样重写它。
  • 还有其他一些 Piotrek 没有提到的错误,但他的代码避免了这些错误。只是为了让您朝着正确的方向开始:(1)在 Clojure 中,没有一个日常常用函数实际上会修改一条数据,正如您的行 (concat list '(line)) 应该做的那样,因为它在 doseq 内. concat不能修改list;相反,它返回一条新数据,即其参数的串联。
  • (2) '(line) 将返回一个包含单个元素的列表,即符号 line,但您想要的是该符号的内容。 line 应该没有括号或引号。
  • (3) 最后一行中的(list) 看起来返回的是在没有参数的情况下调用函数list 的结果,返回的是空列表。当有一个不带引号的列表时,它的第一个参数通常被视为一个函数的名称,该函数将与列表的其他元素作为其参数进行评估(特殊情况除外,如引用列表时)。但是,在这种情况下,您已经重新定义了list,因此它不再引用函数。所以(list) 会产生错误,而不是执行list 通常命名的函数。

标签: recursion clojure functional-programming lisp tail-recursion


【解决方案1】:

首先,您不应该将您的函数定义嵌套在另一个defn(在这种情况下为-main)中。 defndef 总是在命名空间的顶层定义符号绑定并且它们不嵌套。如果你想定义一个局部作用域的函数,你需要使用letfn,例如

(let [my-fn (fn [a b] (+ a b))]
  (my-fn 1 2))

在您的特定情况下,我认为将代码拆分为多个函数会更容易。这样会更易读。

提示输入文件名是您的逻辑之一。

(defn get-existing-filename []
  (let [filename (read-line)]
    (if (.exists (java.io.File. filename))
      filename
      (do
        (println "Sorry, this file doesn't exists. Type a valid file name...")
        (recur)))))

然后您可以使用它来读取删除空行的文件:

(with-open [input (clojure.java.io/reader (get-existing-filename))]
  (->> (line-seq input)
       (remove empty?)
       (doall)))

对于具有以下内容的文件:

AAA

BBB
CCC

DDD

它会返回

("AAA" "BBB" "CCC" "DDD")

如果你真的想要它作为一个单一的功能,以下将起作用:

(defn read-file []
  (let [filename (read-line)]
    (if (.exists (java.io.File. filename))
      (with-open [input (clojure.java.io/reader (get-existing-filename))]
        (->> (line-seq input)
             (remove empty?)
             (doall)))
      (do
        (println "Sorry, this file doesn't exists. Type a valid file name...")
        (recur)))))

终于可以从-main调用这个函数了。

我还注意到您的示例代码中的另一个问题:

((println "Sorry, this file doesn't exists. Type a valid file name...")
 (recur))

ifif-notthenelse 分支需要一个表达式。如果你想拥有多个表达式,你需要将它们嵌套在do

(do
  (println "Sorry, this file doesn't exists. Type a valid file name...")
  (recur))

如果您需要 ifif-not 而没有 else 分支,那么您可以使用 whenwhen-not 宏。那么你就不需要包装多个表达式,因为when/when-not 会将它们包装在do 的内部。

(when true
  (println 1)
  (println 2))

等价于

(if true
  (do
    (println 1)
    (println 2)))

【讨论】:

    猜你喜欢
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    相关资源
    最近更新 更多