Пользователь reddit по имени Алан шутко заявил, что необходимо сделать Scheme , Common Lisp , Haskell и другие не мейнстрим-языки более привлекательными для среднего программиста.
Сравните это с типами простых программ, которые мы видим в Perl и Python. “У меня есть куча файлов, и я хочу переименовать их все по какому-то шаблону.” Общая проблема, простое решение. “У меня есть файл журнала, полный адресов электронной почты, мне нужно удалить их из записей журнала, удалить дубликаты и добавить их в базу данных.” Опять же, довольно простой, довольно маленький, действительно полезный. Когда Haskell сможет конкурировать в таких задачах, будет легче заставить людей изучать его. (То же самое с CL, моим любимым языком….)
Итак, вот схема программы, которая делает это. Он написан для использования MzScheme , потому что это единственная схема, которую я установил в Windows на данный момент. Таким образом, он использует преимущества PLaneT и других библиотек, которые поставляются вместе с MzScheme.
Надеюсь, это убедит других в том, что Scheme-хороший язык для общих задач.
Переименование файлов в схеме
У меня есть куча изображений, которые я загрузил с цифровой камеры, и все они ужасны. DSCxxxx.JPG узор. Давайте переименуем их, чтобы включить название коллекции изображений и дату их создания. Наиболее распространенным способом включения метаданных в изображение является использование EXIF. Я не буду вдаваться во все детали формата, так как это не имеет значения для примера. Все, что нам нужно знать, это то, что метаданные находятся в верхней части файла, файл является двоичным, и мы ищем отметку даты и времени.
Регулярные Выражения
Отметка даты и времени появляется после слова “Выход” в какой-то момент файла и имеет вид:
ГГГГ:ММ:ДД чч:мм:сс
Таким образом, наше регулярное выражение будет выглядеть так:
Exif.+(\d{4}):(\d{2}):(\d{2}).(\d{2}):(\d{2}):(\d{2})
Вот хорошая ссылка, которая поможет при расшифровке приведенного выше регулярного выражения .
Мы переименуем файлы, чтобы они выглядели так:
имя коллекции [year_month] идентификатор изображения
Например, файл DSC02484.JPG
будет переименован в Canada Day [2007_08] DSC02484.JPG
.
При циклическом просмотре файлов нам нужно будет убедиться, что мы переименовываем правильные файлы. Поэтому нам нужно еще одно регулярное выражение для проверки DSC в начале и расширения файла JPG в конце. Это выглядит так:
^DSC.+(jpg|JPG)$
Префикс для изображений, загруженных с вашей камеры, может быть другим, но я использую камеру Sony, и “DSC” – это типичный префикс.
Результат в схеме
Результирующий код таков:
(require (lib "file.ss") (lib "pregexp.ss") (lib "list.ss")) ;; Utility functions ;; Reads lines from the current input port until the end-of-file is reached (define (read-lines) (letrec ((loop (lambda (lines) (let ((line (read-line))) (if (eof-object? line) (reverse lines) (loop (cons line lines))))))) (loop '()))) ;; Reads and returns a list of all lines from a file (define (read-lines-from-file filename) (with-input-from-file filename read-lines)) ;; image? returns true if the file extension ;; of the path f is ".jpg" or ".JPG" and the filename begins with "DSC" (define (image? f) (pregexp-match "^DSC.+(jpg|JPG)$" (path->string f))) (define exif-datetime-regexp (pregexp "Exif.+(\\d{4}):(\\d{2}):(\\d{2}).(\\d{2}):(\\d{2}):(\\d{2})")) ;; The body (define image-collection-name "Canada Day") (define jpg-files (filter image? (directory-list))) (for-each (lambda (file) (for-each (lambda (line) (let ((winner (pregexp-match exif-datetime-regexp line))) (if winner (rename-file-or-directory file (build-path (current-directory) (format "~a [~a_~a] ~a" image-collection-name (list-ref winner 1) (list-ref winner 2) (path->string file))))))) (read-lines-from-file file))) jpg-files)
Первая строка импортирует необходимые библиотеки: pregexp.ss (библиотека регулярных выражений, совместимых с Perl), list.ss, которая содержит функции манипулирования списками, и file.ss, которая имеет полезные функции для манипулирования каталогами и файлами. Одной из полезных функций file.ss, используемых в нашем скрипте, является directory-list , которая возвращает список всех файлов и каталогов в пути (текущий каталог используется, если путь опущен).
Первые две функции-это просто служебные функции, которые я не хотел помещать в другой файл. Они считывают все строки из файла и превращают их в список для использования с map
, for-each
и другими функциями списка.
Следующая функция была объяснена выше. Он используется с функцией фильтра, чтобы отфильтровать файлы, которые нам не нужно переименовывать. После этой функции мы устанавливаем имя коллекции на то, что хотим. Затем возьмите список файлов, которые передают изображение ?
тест.
После этого мы перебираем файлы, а затем перебираем строки в поисках формата даты/времени EXIF. Если мы найдем формат, мы переименуем файл. Мы должны использовать функцию build-path
, чтобы убедиться, что разделители имен файлов работают на платформе, на которой мы находимся. Функция list-ref
используется для выбора частей формата даты/времени, которые мы хотели бы использовать: (list-ref winner 1)
выбирает год и (list-ref winner 2)
выбирает месяц.
По запросу будут предоставлены разъяснения. Этот код кажется самоочевидным, но я уверен, что разработчики Perl/C++/Java чувствуют то же самое по отношению к своему самому запутанному коду.
Это Было Похоже На То, Но Не Было
Такие языки, как Perl, Python и Ruby, позволяют писать ненужные скрипты. Perl был назван языком только для записи, потому что вы пишете сценарий, чтобы исправить что-то, а затем выбрасываете его, потому что вы можете легко переписать его в следующий раз, когда это потребуется. Философия этих языков состоит в том, чтобы дать вам возможность писать простые, распространенные программы как можно проще. Я заметил, что, когда я писал сценарий, я искал полезные функции, которые я мог бы повторно использовать в будущем, когда это будет необходимо.
На днях я написал небольшой скрипт Python для замены определенных строк в определенных файлах. Когда я писал ее, все, о чем я мог думать, – это о том, чтобы починить эти проклятые файлы. Это было так просто, что я не думал о том, как я буду делать это в будущем для других типов файлов. Ответ прост: создайте функцию, которая принимает имя файла и список списков, где каждый подсписок содержит строку для сопоставления и строку для замены. Этот ответ пришел легко после того, как давление было снято. Но с помощью Scheme я всегда ищу макросы и функции для создания , чтобы облегчить потенциальные проблемы, которые у меня могут возникнуть.
В заключение следует отметить, что философия и сообщество, окружающие семейства Lisp и Perl/Python/Ruby, различны. Это хорошо видно по слабым попыткам эмулировать CPAN. Это видно по отсутствию общих задач, выполняемых с помощью Lisp. Но, как я уже показал, это можно сделать…