不在尾部位置重复
Recur not at tail position
如何使用类似于 recur
not at tail position 的东西?
看看我的代码:
(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 环境。所以...
有没有办法在这种情况下使用递归?
有什么替代方案?
首先,您不应该将函数定义嵌套在另一个 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
会return
("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))
if
和 if-not
的 then
和 else
分支需要一个表达式。如果你想有多个表达式,你需要将它们嵌套在 do
:
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
如果您需要 if
或 if-not
而没有 else 分支,那么您可以使用 when
或 when-not
宏。那么你不需要包装多个表达式,因为 when
/when-not
会将它们包装在你的 do
.
内部
(when true
(println 1)
(println 2))
等同于
(if true
(do
(println 1)
(println 2)))
如何使用类似于 recur
not at tail position 的东西?
看看我的代码:
(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 环境。所以...
有没有办法在这种情况下使用递归? 有什么替代方案?
首先,您不应该将函数定义嵌套在另一个 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
会return
("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))
if
和 if-not
的 then
和 else
分支需要一个表达式。如果你想有多个表达式,你需要将它们嵌套在 do
:
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
如果您需要 if
或 if-not
而没有 else 分支,那么您可以使用 when
或 when-not
宏。那么你不需要包装多个表达式,因为 when
/when-not
会将它们包装在你的 do
.
(when true
(println 1)
(println 2))
等同于
(if true
(do
(println 1)
(println 2)))