函数未在地图中调用

时间:2018-03-17 23:05:01

标签: clojure

我正在尝试重命名给定目录中的所有文件和子目录。 我创建旧名称和新名称的向量,并在两个向量上映射重命名函数。 文件路径的向量是创建好但映射 ;;;; Use: Replaces whitespace in names of all directories and files in the given directory with the given separator. (ns file-renamer.core (:gen-class) (:require [clojure.string :as str])) (use '[clojure.java.io]) (use '[clojure.pprint]) ; Constants for testing -> params live (def direc "/home/john/test/") (def separator "-") ; ==================================== (defn traverse-dir "Return a seq of pathnames of directories and files in the given directoryt" [dir-path] (map #(.getAbsolutePath %) (file-seq (file dir-path)))) (defn replace-spaces "Return a copy of string s1 with all sequences of spaces replaced with string s2" [s1 s2] (str/replace s1 #"\s+" s2)) (defn re-name "Rename a file" [old-file new-file] (pprint (str "Renaming: " old-file " ==>> " new-file)) ; put here for debugging (.renameTo old-file new-file)) (defn -main "Map a fn to rename a file over all files and dirs in the given directory" [& args] (let [flist (vec (traverse-dir direc)) new-flist (vec (map #(replace-spaces % separator) flist))] (pprint flist) (pprint new-flist) (map #(re-name (as-file %1) (as-file %2)) flist new-flist))) 似乎根本没有调用重命名功能(我在其中放置了一个打印调用来测试)。 经过大量的调整和搜索后,我仍然对我所缺少的东西感到困惑。

此外,我想知道如何通过逐步调试代码来避免诉诸论坛。

代码:

17 Mar 20:53 /file-renamer ⋄ lein run
["/home/john/test"
 "/home/john/test/test 108"
 "/home/john/test/test 108/    baaaa   rrrr"
 "/home/john/test/test 108/    baaaa   rrrr/Open    Document Text  ....  odt"
 "/home/john/test/test 108/    baaaa   rrrr/ba   z  z  er   ."
 "/home/john/test/test 108/    baaaa   rrrr/ba   z  z  er   ./freed.frm"
 "/home/john/test/test 108/    baaaa   rrrr/ba   z  z  er   ./New Folder"
 "/home/john/test/test 108/    baaaa   rrrr/ba   z  z  er   ./New Folder/Plain Text.txt"
 "/home/john/test/test 108/    baaaa   rrrr/ba   z  z  er   ./fr   ed.txt"
 "/home/john/test/test 108/s p a c e s------S P A C E S "
 "/home/john/test/fox"
 "/home/john/test/foo"
 "/home/john/test/fog"]
["/home/john/test"
 "/home/john/test/test-108"
 "/home/john/test/test-108/-baaaa-rrrr"
 "/home/john/test/test-108/-baaaa-rrrr/Open-Document-Text-....-odt"
 "/home/john/test/test-108/-baaaa-rrrr/ba-z-z-er-."
 "/home/john/test/test-108/-baaaa-rrrr/ba-z-z-er-./freed.frm"
 "/home/john/test/test-108/-baaaa-rrrr/ba-z-z-er-./New-Folder"
 "/home/john/test/test-108/-baaaa-rrrr/ba-z-z-er-./New-Folder/Plain-Text.txt"
 "/home/john/test/test-108/-baaaa-rrrr/ba-z-z-er-./fr-ed.txt"
 "/home/john/test/test-108/s-p-a-c-e-s------S-P-A-C-E-S-"
 "/home/john/test/fox"
 "/home/john/test/foo"
 "/home/john/test/fog"]
17 Mar 20:53 /file-renamer ⋄ 

输出:

{{1}}

3 个答案:

答案 0 :(得分:6)

这里有几个问题。它们不是你的错,但更多的是REPL正在对你发挥作用,你并没有完全理解(可能)Clojure和懒惰操作/序列的核心概念。

map函数返回一个懒惰序列,需要在某个时间点实现,而你没有这样做。最重要的是,重命名功能不是副作用自由功能(纯函数)。 REPL也在玩弄你:如果你在REPL上调用(-main),它会自动实现这些序列,并且会对Clojure的新手造成很多混淆。

最直接的解决方案是使用doall函数。

(doall (map #(re-name (as-file %1) (as-file %2)) flist new-flist))

但这是快速而肮脏的方式,我将在这里引用Stuart Sierra:

  

您可能会得到一些建议,您可以“强制”使用doall或dorun来评估惰性序列。还有一些片段浮动,声称“unchunk”序列。

     

在我看来,doall,dorun甚至“unchunk”的存在几乎总是一个标志,一开始就不应该是一个懒惰的序列。

在这种情况下,更好的解决方案是使用doseq函数并编写如下内容:

(defn -main
    "Map a fn to rename a file over all files and dirs in the given directory"
    [& args]
    (doseq [file-string (traverse-dir direc)]
      (let [input-file (as-file file-string)
            output-file (as-file (replace-spaces file-string separator))]
        (re-name input-file output-file))))

这也可以写得更短。

Stuart Sierra的博客文章是一篇很有帮助的好读物: Clojure Don’ts: Lazy Effects

答案 1 :(得分:3)

检查doall上的文档。

这里,重命名是一种副作用。地图返回一个惰性序列。您需要强制映射整个集合,以便重命名。

答案 2 :(得分:2)

您可以像其他人建议的那样强制评估map使用doall返回的延迟序列(或者只使用严格mapv),但map isn&# 39; t旨在实施副作用。添加doall可以让它发挥作用,但它只是掩盖了气味。

我为此使用doseq,因为您只是迭代序列来执行副作用。你实际上并不关心map评估的内容。不幸的是,这个解决方案需要明确地将两个集合压缩在一起,然后才能将它们提交给doseq,这会使它大量增加:

(let [zipped (map vector flist new-flist)] ; Zip the sequences together
  (doseq [[f1 f2] zipped]
    (re-name (as-file f1) (as-file f2)))

它并不紧张,但doseq的使用使得意图更加清晰。