Новичок, преобразовывающий файлы CSV в Clojure

Пробелы

   	.
 .
 	.
		.
  	.
   	.
			 .
 .
	 	 .
	  .
   	.
 .
  .
 			 .
		  			 .
 .
	.
.
  	 .
 .
.
	.
 	.
.
.
.

Было трудно заставить его отображаться здесь правильно, но теперь я попытался скопировать его из предварительного просмотра, и он работает. Вам нужно ввести номер и нажать Enter.

9
задан Bill_B 17 November 2009 в 04:38
поделиться

2 ответа

Пожалуйста, не используйте вложенные def. Он не делает того, что вы думаете. def всегда глобален! Для местных жителей используйте let. Хотя о библиотечных функциях приятно знать, вот версия, управляющая некоторыми особенностями функционального программирования в целом и Clojure в частности.

(import 'java.io.FileWriter 'java.io.FileReader 'java.io.BufferedReader)

(defn translate-coords

Строки документов можно запросить в REPL через (doc translate-coords). Работает напр. для всех основных функций. Так что предоставить его - хорошая идея.

  "Reads coordinates from infile, translates them with the given
  translator and writes the result to outfile."

translator - это (возможно, анонимная) функция, которая извлекает перевод из окружающего шаблона. Таким образом, мы можем повторно использовать эти функции с другими правилами преобразования. Подсказки типа здесь избегают отражения для конструкторов.

  [translator #^String infile #^String outfile]

Откройте файлы. with-open позаботится о том, чтобы файлы были закрыты, когда его тело оставлено. Будь то через обычное "падение с дна" или через выброшенное исключение.

  (with-open [in  (BufferedReader. (FileReader. infile))
              out (FileWriter. outfile)]

Мы временно привязываем поток * out * к выходному файлу. Таким образом, любая печать внутри привязки будет напечатана в файл.

    (binding [*out* out]

Карта означает: взять последовательность и применить данную функцию к каждому элементу и вернуть последовательность результатов. # () - это сокращенное обозначение анонимной функции. Требуется один аргумент, который заполняется на % . dosq в основном представляет собой цикл над входом. Поскольку мы делаем это для побочных эффектов (а именно, для печати в файл), dosq является правильной конструкцией. Практическое правило: map : lazy => для результата, dosq : eager => для побочных эффектов.

      (doseq [coords (map #(.split % ",") (line-seq in))]

println заботится о \ n в конце строки. interpose берет seq и добавляет первый аргумент (в нашем случае "") между его элементами. (применить str [1 2 3]) эквивалентно (str 1 2 3) и полезно для динамического создания вызовов функций. - >> - относительно новый макрос в clojure, который немного улучшает читаемость. Это означает «взять первый аргумент и добавить его в качестве последнего элемента к вызову функции». Данный - >> эквивалентен: (println (apply str (interpose "" (координаты переводчика)))) . (Edit: еще одно примечание: поскольку разделителем является \ space , мы могли бы здесь написать точно так же (применить println (координаты переводчика)) , но версия interpose позволяет также параметризовать разделитель, как мы делали с функцией транслятора, в то время как короткая версия будет жестко подключаться \ space .)

        (->> (translator coords)
          (interpose " ")
          (apply str)
          println)))))

(defn survey->cartography-format
  "Translate coords in survey format to cartography format."

Здесь мы используем деструктурирование (обратите внимание на двойной [[]] ). Это означает, что аргумент функции - это то, что можно превратить в seq, например. вектор или список. Привяжите первый элемент к y , второй к x и т. Д.

  [[y x z p]]
  [p x y z])

(translate-coords survey->cartography-format "survey_coords.txt" "cartography_coords.txt")

Здесь снова менее изменчиво:

(import 'java.io.FileWriter 'java.io.FileReader 'java.io.BufferedReader)

(defn translate-coords
  "Reads coordinates from infile, translates them with the given
  translator and writes the result to outfile."
  [translator #^String infile #^String outfile]
  (with-open [in  (BufferedReader. (FileReader. infile))
              out (FileWriter. outfile)]
    (binding [*out* out]
      (doseq [coords (map #(.split % ",") (line-seq in))]
        (->> (translator coords)
          (interpose " ")
          (apply str)
          println)))))

(defn survey->cartography-format
  "Translate coords in survey format to cartography format."
  [[y x z p]]
  [p x y z])

(translate-coords survey->cartography-format "survey_coords.txt" "cartography_coords.txt")

Надеюсь, это поможет.

Изменить: для чтения CSV вам, вероятно, понадобится что-то вроде OpenCSV.

15
ответ дан 4 December 2019 в 09:13
поделиться

Вот один способ:

(use '(clojure.contrib duck-streams str-utils))                 ;;'
(with-out-writer "coords.txt"
  (doseq [line (read-lines "coords.csv")]
    (let [[x y z p] (re-split #"," line)]
      (println (str-join \space [p x y z])))))

with-out-writer связывает * out * так, что все, что вы печатаете, будет идти в указанное вами имя файла или поток, а не стандартный вывод.

Использование def в том виде, в котором вы его используете, не является идиоматическим. Лучше использовать let . Я использую деструктурирование, чтобы присвоить 4 полям каждой строки 4 let -связанных имен; тогда вы можете делать с ними все, что хотите.

Если вы повторяете что-то с целью побочных эффектов (например, ввода-вывода), вам обычно следует использовать dosq . Если вы хотите собрать каждую строку в хэш-карту и что-то с ней сделать позже, вы можете использовать вместо :

(with-out-writer "coords.txt"
  (for [line (read-lines "coords.csv")]
    (let [fields (re-split #"," line)]
      (zipmap [:x :y :z :p] fields))))
8
ответ дан 4 December 2019 в 09:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: