Pull to refresh

Работа с умом, а не руками: пример увеличения производительности редактирования текста в Emacs

Reading time2 min
Views2K

История


Я почти закончил один проект и, как это всегда бывает, в последний момент потребовалось внести массовые рутинные изменения.

Это был Bash-скрипт для автоматизации процесса сборки на различных Unix-ах и, реальность такова, что между моей средой и средой сервера сборки небыло ничего общего.

Вывод был один: надо было использовать абсолютные пути к утилитам, которые встречались в скрипте. Для этого я решил использовать переменные, как в Makefile GNU Autoconf: “RM=/usr/local/bin/rm” и “${RM} -vf foo”.

Работа почти сделана, но у меня 3 таких скрипта, которые выглядят очень похоже и я совершенно не хочу делать эти замены руками… поэтому я попытался сделать версию `replace-string’ в Emacs-овом *scratch* буфере, как раз для моей проблемы:

(defun foo ()
  (interactive)
  (let ((begin (region-beginning))
	(end   (region-end)))
    (mapcar #'(lambda (p)
		(replace-string (format "%s " (cadr p))
				(format "${%s} " (car p))
				nil begin end))
	    '((GNUTAR tar)
	      (GUNZIP gunzip)
	      (GZIP gzip)
	      (GMAKE gmake)
	      (AUTORECONF autoreconf)
	      (MKDIR mkdir)
	      (ECHO echo)
              (CP cp)
	      (FIND find)
	      (RM rm)
	      (TEST test)
	      (LN ln)))))

И это всё!

Немного по-подробней, что это за код


Это функция `foo', которую можно вызывать в редакторе интерактивно, т.е. во время редактирования текста, а не только из тела других функций. Она расширяет действие функции `replace-string’ тем, что вызывает её для каждого элемента из списка:
             ((GNUTAR tar)
	      (GUNZIP gunzip)
	      (GZIP gzip)
	      (GMAKE gmake)
	      (AUTORECONF autoreconf)
	      (MKDIR mkdir)
	      (ECHO echo)
              (CP cp)
	      (FIND find)
	      (RM rm)
	      (TEST test)
	      (LN ln))

Как результат, заменяя найденное в тексте второе значение каждого элемента на первое (например "tar" на "GNUTAR").

В начале своего выполнения, функция `foo' определяет границы выделенного региона, в котором ей следует произвести замены: это вызовы `region-beginning' и `region-end'.

Затем происходит вызов функции `mapcar', которая вызывает функцию-замыкание `lambda (p)' для каждого элемента списка.

Задача функции-замыкания состоит в том, чтобы разобрать переменную `p' на составляющие и вызвать функцию `replace-string' для замены в тексте.

Эпилог


Замечу, что функции семейства `map' могут оперировать над произвольным количеством списков и, на первый взгляд, кажется, что вместо списка списков можно было использовать два спика значений: (GNUTAR GUNZIP GZIP ...) и (tar gunzip gzip ...), но такая организация структуры данных, очевидно, более сложна из-за неявных связей между двумя списками, тогда как в предложенном варианте такая связь не выходит за рамки подсписка.

Выводы


Рассмотренная функция представляет собой одноразовую конструкцию, призванную решить появившуюся перед одним человеком проблему. Универсализация этой конструкции видится автору бессмысленным занятием. Целью этого поста было показать простоту и лёгкость программирования инструмента (редактора Emacs), которым может пользоваться человек, если он хочет работать с умом, а не руками.
Tags:
Hubs:
+20
Comments26

Articles