0

Как я могу использовать что-то похожее на recur не в положении хвоста?Рекурсия не в положении хвоста

Посмотрите на мой код:

(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, и я исхожу из контекста ООП. Итак ...

Есть ли способ использовать рекурсию в этом случае? Что было бы альтернативой?

+0

Ваша цель, чтобы написать функцию чтения строк текста из файла или существующей функции будет достаточно хорошо для вас? –

+0

Ну ... Функция, которая возвращается, если файл существует уже существует. Я хочу вызвать функцию readFile до тех пор, пока не будет указано имя файла. –

+1

Хотя вы этого не писали, логика вашей функции кажется «если <файл не существует», то else <делать вещи, предполагающие существование файла>>, и в этом случае функция * является хвостовой рекурсивной. Поэтому я предлагаю переписать его так. – tfb

ответ

6

Прежде всего, вы не должны определять свои функции в другом 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)) 

if и if-not требуют одно выражение для их then и else ветвей. Если вы хотите иметь несколько выражений, вы должны вкладывать их в do:

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

Если вам нужно if или if-not без еще филиала, то вы можете использовать when или when-not макросы. Тогда вам не нужно обертывать несколько выражений, потому что when/when-not обернет их для вашей внутренней части do.

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

эквивалентно

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