我正在尝试重命名给定目录中的所有文件和子目录。
我创建旧名称和新名称的向量,并在两个向量上映射重命名函数。
文件路径的向量是创建好但映射
;;;; 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}}
答案 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
的使用使得意图更加清晰。