Recursion 不在尾部位置复发

Recursion 不在尾部位置复发,recursion,clojure,functional-programming,lisp,tail-recursion,Recursion,Clojure,Functional Programming,Lisp,Tail Recursion,如何使用类似于重复出现的不在尾部位置? 看看我的代码: (defn-main[&args] (println“嗨!键入文件名…”) (defn readFile[]) (让[文件名(读取行)] (让[rdr(读卡器文件名)] (如果不存在(.rdr) ((println“抱歉,此文件不存在。请键入有效的文件名…”) (再次出现) (定义列表'()) (doseq[行(行顺序rdr)] (如果不是(“)行) (concat列表(行))) (名单) (defn fileLinesList(readF

如何使用类似于
重复出现的
不在尾部位置?

看看我的代码:

(defn-main[&args]
(println“嗨!键入文件名…”)
(defn readFile[])
(让[文件名(读取行)]
(让[rdr(读卡器文件名)]
(如果不存在(.rdr)
((println“抱歉,此文件不存在。请键入有效的文件名…”)
(再次出现)
(定义列表'())
(doseq[行(行顺序rdr)]
(如果不是(“)行)
(concat列表(行)))
(名单)
(defn fileLinesList(readFile))
...
...)
我知道我不能在这里使用
recur
。。。但我都不知道我怎样才能在clojure做到

我是Clojure的新手,来自OOP环境。所以

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

首先,您不应该将函数定义嵌套在另一个
defn
-main
)中
defn
def
始终在名称空间的顶层定义符号绑定,它们不会嵌套。如果要定义局部作用域函数,需要使用
let
fn
,例如

(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))
如果
如果不
要求其
分支使用单个表达式,则
否则
分支。如果要有多个表达式,需要将它们嵌套在
do

(do
  (println "Sorry, this file doesn't exists. Type a valid file name...")
  (recur))
如果您需要
If
If not
而不使用else分支,则可以在
时使用
时使用宏。然后,您不需要包装多个表达式,因为
when
/
when not
将为您的
do
内部包装它们

(when true
  (println 1)
  (println 2))
相当于

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

首先,您不应该将函数定义嵌套在另一个
defn
-main
)中
defn
def
始终在名称空间的顶层定义符号绑定,它们不会嵌套。如果要定义局部作用域函数,需要使用
let
fn
,例如

(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))
如果
如果不
要求其
分支使用单个表达式,则
否则
分支。如果要有多个表达式,需要将它们嵌套在
do

(do
  (println "Sorry, this file doesn't exists. Type a valid file name...")
  (recur))
如果您需要
If
If not
而不使用else分支,则可以在
时使用
时使用宏。然后,您不需要包装多个表达式,因为
when
/
when not
将为您的
do
内部包装它们

(when true
  (println 1)
  (println 2))
相当于

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

您的目标是编写一个从文件或现有函数读取文本行的函数吗?虽然您没有这样编写函数,但函数的逻辑似乎是“if-then-else”,在这种情况下,函数是尾部递归的。所以我建议这样重写它。还有其他一些Piotrek没有提到的错误,但是他的代码避免了这些错误。为了让您从正确的方向开始:(1)在Clojure中,日常常用函数都不会像您的行
(concat list'(line))
那样修改数据,因为它位于
doseq
内部<代码>目录
无法修改
列表
;相反,它返回一个新的数据段,这是其参数的串联。(2)
(行)
将返回一个包含单个元素的列表,即符号
,但您需要的是该符号的内容。应该存在的内容是没有括号或引号的
。(3)最后一行中的
(list)
返回的内容是在没有参数的情况下调用函数
list
,返回空列表的结果。当有一个不带引号的列表时,它的第一个参数通常被视为一个函数的名称,该函数将与列表中的其他元素一起作为它的参数进行计算(特殊情况下除外,如列表带引号时)。但是,在本例中,您重新定义了
列表
,因此它不再引用函数。因此
(list)
将产生错误,而不是执行
list
通常命名的函数。您的目标是编写一个函数,从文件或现有函数中读取文本行,这对您来说足够了吗?虽然您没有这样编写,但函数的逻辑似乎是“if then else”,在这种情况下,函数是尾部递归的。所以我建议这样重写它。还有其他一些Piotrek没有提到的错误,但是他的代码避免了这些错误。为了让您从正确的方向开始:(1)在Clojure中,没有任何日常常用的函数会实际修改一段数据,因为您的行
(concat list'(行)