函数未在地图中调用

Joh*_*ohn 2 clojure

我试图重命名给定目录中的所有文件和子目录.我创建旧名称和新名称的向量,并在两个向量上映射重命名函数.文件路径的向量是创建好的,但映射 (map #(re-name (as-file %1) (as-file %2)) flist new-flist)))似乎根本没有调用重命名函数(我已经在其中放置了一个打印调用来测试).经过大量的调整和搜索后,我仍然对我所缺少的东西感到困惑.

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

码:

;;;; 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)))
Run Code Online (Sandbox Code Playgroud)

输出:

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 ? 
Run Code Online (Sandbox Code Playgroud)

小智 6

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

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

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

(doall (map #(re-name (as-file %1) (as-file %2)) flist new-flist))
Run Code Online (Sandbox Code Playgroud)

但这是快速而肮脏的方式,我将在这里引用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))))
Run Code Online (Sandbox Code Playgroud)

这也可以写得更短.

斯图尔特·塞拉的博客文章是一篇很有帮助的好读物: Clojure Don'ts:Lazy Effects.