Быстрое руководство Emacs Lisp
Заметки на полях о emacs lisp

В общем перечитав бегло руководство по emacs "An Introduction to Programming in Emacs Lisp" переведено на русский СПАСИБО тем людям. Также отталкиваясь от книжки Little Schemer. C-x C-e запустить выражение после скобки которого стоит курсор. Поехали.

Лисп язык в основу которого положена идея что все есть список. Точнее сущности две это список и атом. На практике в памяти список представляет собой последовательность связанных указателями структур, последний элемент указывает в nil(null в лиспе). Забавно мы в 11 классе на cpp делали кольца и очереди, это было прикольно в качестве организации памяти, но вот в чего это может вылезти.

В общем elisp это язык с очень неплохой встроенной системой привязанной к редактору. К сожалению официальное интро скачет рассказывая принцип языка, а потом переключаясь к возможностям редактора.

Содержание

1 Примитивы и лиспа

1.1 Атом и список

Знак ' обозначает команду интерпретатору не вычислять значение записанное по данному имени.

Все есть атом

(atom 'a)
(atom nil)
(atom ())
;; Ниже ложь nil(false) 
(atom '(1 2 3))
(progn
  (set 'a '(1 2 3))
  (atom a))

Пустота пусто значение nil = null(лисп) = ()

(null ())
(null '())
(null nil)
;; False ниже
(null 'a)
(null '(1 2 3))

Ну и проверка на список есть обратная функция not listp -> nlistp

(listp '(1 2 3))
(listp nil)
(listp ())
;; Ниже false
(listp 'a)
(listp buffer-saved-size)

1.2 Примитивы работы со списком

Атом наименьшая делимая единица по мнению лиспа и список состоящий из этих атомов. Символ лиспа это ящерица или змея, т.е. нечто длинное с большой головой это и есть идее S-выражений(атом + списки) список начало/голова которого некая функция. Поэтому чтобы отделить Голову от остального существуют примитивные функции.

car отрубает голову списка

(car '(1 2 3))                          ;1
(car '((1 2 3)))                        ;(1 2 3)
(car (car '((1 2 3) 4 5)))              ;1
(car ())                                ;nil голова у пустого списка отсутствует
(car 'a)                                ;error , принимает только не пустые списки

cdr возвращает оставшийся хвост списка без головы

(cdr '(1 2 3))                  ;(2 3)
(cdr '(1))                              ;nil
(cdr '())                               ;nil
(cdr 'a)                                ;error , принимает непустые списки, ну почти

cons вставляется атом перед списком.

(cons 'мама '(мыла раму))               ;(мама мыла раму)
(cons '(если мама) '(мыла раму))        ;((если мама) мыла раму)
(progn
  (set 'a '(если мама))
  (cons a '(мыла раму)))                ;((если мама) мыла раму)
(cons () '(void answer))                ;(nil void answer)
(cons 'a 'b)                            ; (a . b) точечная пара
(progn
  (set 'a 's)
  (set 'b 'w)
  (cons a b))                           ; (s . w)

В общем ЗДЕСЬ РАСХОЖДЕНИЕ С УЧЕБНИКОМ т.к. в чистом лиспе , там где ответ точечная пара, должна быть ошибка.

1.3 Функция

По функции hello. Для того чтобы функция запускалась через M-x обязательно нужен interactive.

По функции lat? это рекурсивная функция которая идет по всему списку, отщупывая его начало. Из непонятного это функция cond она работает следующим образом, выбирается первый элемент списка, если он t, выполняется действие и функция выходит из cond. Если нет cond проверяет следующее условие.

(defun hello()
  "Hello world function"
  (interactive)
  (message "Hello world!"))
;; Это прост, а вот ниже из учебника
(defun lat? (l)
  "l - list of atoms?"
  (cond
   ((null l) t)
   ((atom (car l)) (lat? (cdr l)))
   (t nil)))

(lat? '(1 2 3))                 ;t
(lat? '((1 5) 2 3))                     ;nil

Вернуть функцию как объект можно

(function +)
(apply '+ 1 2 '(3 4))

1.4 Условия и ветвления

(if t (message "True") (message "Else")) ;True
(if nil (message "True") (message "Else")) ;Else

(progn
  (setq a 'tea)
  (setq lat '(tea with lemon))
  (if (eq a (car lat))
      (message "True") (message "Else")) ;True
  )

Разные всякие условия, member возвращает список начинающийся с искомого символа, это отличие от книги

(eq 'a 'b)                              ;nil
(member '3 '(1 2 3 4 5))                ;(3 4 5)

(or (eq 'a 'b)                          
(member '3 '(1 2 3 4 5)))               ;(3 4 5)
(and (eq 'a 'b)                         
(member '3 '(1 2 3 4 5)))               ;nil

Построение списков

(defun rember (a lat)
  "Удаляет первый встреченный a атом в lat"
    (cond
     ((null lat) (quote ()))
     ((eq (car lat) a) (cdr lat))
     (t (cons (car lat)
              (rember a (cdr lat))))))

(rember '1 '(1 2 3 4 1 2 3))

1.5 Итерационные операторы

mapcar maplist

1.6 TODO Типы данных

Вот тут главный вопрос они есть или хотя бы есть проверка на соответствие объекта типу. Как же они задаються.

вы можете получить символ, описывающий заданный объект с помощью функции type-of.

В Emacs, как и в других Lisp‘ах, имеется понятие символа (symbol) – объекта с уникальным именем. Каждый символ имеет четыре компонента – печатное имя, значение (value), связанную функцию и список свойств (property list). Определение символов производится с помощью различных выражений, которые определяют как этот символ будет использоваться 13 . defvar & defconst определяют символ как глобальную переменную или константу

1.7 Списки

Можно работать как с массивом, можно как с Вектором можно как с Stack

  • логические nil t
  • числа -2^29-1:2^29
  • doube C
  • символы ?2 ?a ?\n ?\u5647
  • "строки"
  • Точечные пары (la . da)
  • Список (1 2 3) = (1 . (2 . (3 . nil)))
  • Словарь ((red . fasta) (greenskin . boys) (gork . mork))

Проверка типов listp, integerp,symbolp,bufferp

Узнать тип переменной type-of

1.8 Связанные с редактором понятия

Точка point Метка mark (второй конец выделения это точка point) Область region выделенный текст в буфере ограниченный точкой меткой. Буфере

1.9 Переменные

(setq a 17)
(setq s "Hello!")
что эквивалентно:
(defvar a 17)
(defvar s "Hello!")

defvar & defconst определяют символ как глобальную переменную или константу.Одним из преимуществ использования defvar & defconst является возможность добавления документации к символу.

1.9.1 Локальные

Локальные переменные объявляются при помощи функций let и let*.

(let (a b (c 3))
  (setq a 1
      b 2)
(message (number-to-string (+ a b c))))

1.10 Последовательное выполнение команд

progn Возвращает результат последней команды из последовательности

1.11 Условный оператор

Форма

(if <условие> <тогда> <иначе>)
Специальная форма if работает так же, как и в любом другом языке
программирования. Параметр <иначе> является необязательным.
(let ((a 5) b)
   (if (> a 0)
      (progn
          (setq b 17)
          (message (number-to-string b)))
(message "The ELSE branch")))

Условия:

  • (eq obj1 obj2) – возвращает t если obj1 и obj2 являются одним и тем же объектом, целым числом с одинаковым значением, или пустой строкой;
  • (eql obj1 obj2) – аналогично eq, но также работает и для чисел с плавающей точкой;
  • (equal obj1 obj2) – возвращает t если obj1 и obj2 имеют одинаковые компоненты, что позволяет использовать ее для сравнения списков, строк и других объектов.

К специализированным функциям сравнения относятся: , /, <, >, <=, >= – для сравнения чисел; char-equal, string=, string< – для сравнения строк и букв;

1.12 Объявление функций

Есть два способа объявить функцию. Первый, как мы уже видели ранее, при помощи defun:

(defun <function-name> (<parameters>)
<documentation string>
<body>)

Второй способ – при помощи макроса lambda создать анонимную функцию.

(lambda (<parameters>)
<documentation string>
<body>)

Результатом вычисления будет тело самой функции. Поскольку никакого имени ей не присваивается, то лямбда функцию можно либо сохранить в символе при помощи setq, либо вызвать ее в funcall или mapcar.

(setq l-hello (lambda () (message "Hello, World!")))
(defun hello2 ()
(interactive)
(funcall l-hello))

1.13 Switch case оператор

(let ((a 5))
(cond
((> a 0) (message "Positive"))
((= a 0) (message "Zero"))
((< a 0) (message "Negative"))))

1.14 Циклы

(while <условие> <тело-цикла>) Специальная форма while работает также как и в любом другом языке программирования.

(let ((s "")
      (i 0))
     (while (< i 10)
            (setq s (concat s (number-to-string i)))
            (setq i (1+ i)))
     (message s))

1.15 Структуры

;;структуры
(defstruct point
  x
  y)

(setf p (make-point :x 0 :y 0))
(point-x p)
(point-y p)
(point-p p)

1.16 TODO Функции

;;rect и прочее

1.17 TODO Символы

(symbol-name 'abc)
(symbol-plist 'buffer)       ;свойства символа

1.18 Работа в emacs

Перейти к заданию этой переменной M-. (тэгу)

В зависимости от того как обстоят дела с вашим дистрибутивом Emacs, вам может понадобится описать `таблицу тегов' (tag table), которая обычно хранится в файле `TAGS'. Таблица, которую вы наверняка захотите описать, находится в каталоге `emacs/src'; то есть вы должны использовать команду M-x visit-tags-table, и задать в мини-буфере полный путь к этому файлу например `/usr/local/lib/emacs/19.23/src/TAGS'. See section `Tag Tables' in The GNU Emacs Manual. Также смотрите Create Your Own `TAGS' File, для получения дополнительной информации о том как самому создать таблицу тегов.

После того как вы поближе ознакомитесь с Emacs Лиспом, вы часто будете использовать find-tags для перемещения по исходным кодам Emacs; и часто будете создавать свои собственные таблицы `TAGS'.

Показывать парные скобки всегда а не только по закрытию скобки. M-x show-paren-mode https://www.emacswiki.org/emacs/ShowParenMode

  (message "All about you")             ;Печатаем в message буфер.
  (buffer-name)"*scratch*"              ;C-u C-x C-e выполнить выражение и вывести результат после курсора.
  (switch-to-buffer (other-buffer))     ;сменить буфер

  (buffer-size)
  (point)
  (point-max)
  (point-min)

  (save-excursion)                      ;Сохранить текущие полежения точки и метки.
  (print-region)


;; Напишите не интерактивную функцию, которая удваивает значение своего аргумента, числа. Сделайте эту функцию интерактивной.


(defun example-twice (d)
  "Удваивает число"
  (* 2 d))

(example-twice 4)


(defun example-twice (d)
  "Удваивает число интерактивно"
  (interactive "N")
  (message "Итог %d" (* 2 d)))

;; Напишите функцию, которая проверяет: больше ли текущее значение
;; fill-column, чем значение аргумента, переданного в функцию, и если
;; да, то печатает соответствующее сообщение в эхо-области.

(defun fillbetter (n)
  "fill-column, чем значение аргумента, переданного в функцию"
  (interactive "N")
  (if (> fill-column n) (message "Да больше")))

Интерактивная функция вводит число не через 'p' а через 'N'

C-h p Поиск по файлам библиотек, по факту же просто показано какие пакеты встроенв, на самом деле find-library

1.18.1 TODO Метки и закладки

Вообще довольно много всяких объектов так или иначе закольцованных тот же буфер вставок или по другому kill-ring.

В общем в emacs есть такая опция как установить метку, C-SPC и переместиться к ней C-u C-SPC

Есть опция установить закладку и к ней переместиться bookmark, но закладки глобальны и привязаны к файлам и положениям в них.

1.18.2 TODO Сужения

http://www.emacs.uniyar.ac.ru

Таки не понял картина отображающая буфер в окне использует сужение или нет, так и не понял зачем этот костыль?

1.18.3 Проверка типов

(bufferp (get-buffer "*scratch*"))
(listp ())
(integerp 1) 

В общем есть тип проверка типа - probe. Поэтому берем тип добавляем p в конце и проверяем.

1.18.4 Необязательные аргументы функции

(defun beginning-of-buffer (&optional arg)
  "Переместить точку в начало буфера;
оставив метку в предыдущей позиции курсора.
С арг N, переместить точку в N/10 от начала
буфера.
Не используете эту функцию в своих программах!
\(goto-char (point-min)) быстрее и не устанавливает
метку."
  (interactive "P")
  (push-mark)
  (goto-char 
   (if arg
       (if (> (buffer-size) 10000)
           ;; Avoid overflow for large buffer sizes!
           (* (prefix-numeric-value arg)
              (/ (buffer-size) 10))
         (/ (+ 10 (* (buffer-size) 
                     (prefix-numeric-value arg))) 
            10))
     (point-min)))
  (if arg (forward-line 1)))


(beginning-of-buffer 5)

interactive P берет префикс аргумент, т.е. то что мы передали функции. prefix-numeric-value преобразователь типов. \ - неинтерпретируемая строка кода в документации функции.

1.18.5 Interactive взаимодействие и передача префикса

Вторая часть "*p\ncZap to char: " — это `p'. Эта часть оканчивается символом новой строки, `\n'. `p' означает, что первым аргументом передаваемым функции будет значение `префикс-аргумента'. Префикс-аргумент передается нажатием C-u и затем вводом числа или набором M- и числа. Если вы вызвали функцию интерактивно, без задания префикса, то в функцию будет передано значение 1

Вот функция с одним необязательным префиксом, который не вызвать через M-x Функция M-z (zap-to-char 2 (string-to-char "f")) чтобы вызвать её интерактивно нужно набрать C-u 2 M-z

1.18.6 С биндинги и взаимодействие с внешним миром.

В главе 8

del_range — это довольно сложная функция, которую мы не будем здесь рассматривать. Однако все же стоит взглянуть на два аргумента, которые передаются del_range. Это XINT (b) и XINT (e). Когда дело доходит до языка С, b и e — два тридцати двух-битные целых числа, которые отмечают начало и конец области текста для удаления. Но как и другие числа в Emacs Lisp, из тридцати двух бит для хранения числа используются только 24; оставшиеся восемь бит используются для хранения информации о типе объекта, и некоторых других целях. (На некоторых машинах используются только 6 бит). В нашем случае восемь бит используются для записи того, что эти числа — позиции в буфере. Когда биты числа используются для таких целей их называют тег. Использование восьми-битовых тегов для каждого тридцати-двух битового целого делает возможным Emacs исполнятся быстрее чем раньше. С другой стороны, поскольку числа ограничены только двадцатью четырьмя битами, то буферы Emacs ограничены приблизительно восемью мегабайтами. (Вы можете резко увеличить максимальный размер буфера изменив определения VALBITS и GCTYPEBITS в `emacs/src/config.h' до компиляции. Смотрите замечание в `emacs/etc/FAQ' который входит в дистрибутив Emacs).

1.19 Отладка и настройка, поиск документации

Вы можете посмотреть текущее значение любой переменной с помощью функции describe-variable, которая обычно запускается сочетанием клавиш C-h v.

Вы можете вызвать консоль наподобии питона M-x ielm

C-h f - describe function

1.20 Макросы

Как и в C макромы раскрываются на этапе компиляции. Для передачи параметров используются спецсимволы, как то выполнение '`' подстановка ,переменной, раскрытие списка @. За примеры спасибо видео1.

  (defmacro macro1 (name)
    name)

  (defmacro macro2 (name)
    `(list ,name ,name))

  ;; ELISP> (macroexpand '(macro2 123))
  ;; (list 123 123)

  ;; ELISP> (macro2 123)
  ;; (123 123)


  (defmacro macro3 (&rest body)
    `(list ,@body))

  ;; ELISP> (macro3 1 2 3)
  ;; (1 2 3)


(defmacro macro4 (&rest body)
  (let ((res nil))
    (dolist (elem body)
      (setq res (cons (list 'setq (nth 1 elem) (nth 0 elem)) res)))
    `(progn ,@res)))

;; ELISP> (macroexpand '(macro4 (1 a) (2 b)))
;; (progn
;;   (setq b 2)
;;   (setq a 1))
;; ELISP> a
;; 1 (#o1, #x1, ?\C-a)
;; ELISP> b
;; 2 (#o2, #x2, ?\C-b)


2 Объектно ориентированное программирование через EIEIO

В примере создаем класса Helloworld, поле user с значением по умолчанию Vova, также создаем метод класса h-message который выводит поле user.

Функции oref и oset читают и задают значение полей.

(defclass HelloWorld ()
  ((user :initarg :user
         :initform "Vova")))

(defmethod h-message ((this HelloWorld))
  (message (concat "Hello, " (oref this user))))

;; ELISP> (setq h (HelloWorld "123" :user "Dima"))
;; #<HelloWorld HelloWorld-15c7848>
;; ELISP> (oref h user)
;; "Dima"
;; ELISP> (oset h user "Ivan")
;; "Ivan"
;; ELISP> (oref h user)\
;; *** IELM error ***  More than one sexp in input
;; ELISP> (oref h user)
;; "Ivan"
;; ELISP> (h-message h)
;; "Hello, Ivan"

3 Терминология

  • frame — window, окно. Фрейм может содержать несколько окон (emacs window).
  • buffer  — file, объект в памяти, в котором хранится текст. Выглядит как окно.
  • window  — tab/pane, window, тоже окно. Область фрейма отображающая буфер.
  • active region  — text selection, выбор текста (пометка)
  • fill  — word wrap, выравнивание текста
  • yank — paste, вставить скопированное
  • kill — cut, вырезать
  • kill ring — clipboard, область данных где хранится скопированное
  • mode line — status bar, строка отображающая текущие режимы emacs
  • point — cursor, курсор
  • font lock — syntax coloring, подстветка синтаксиса

show-paren-mode

3.1 Не разобрано

  • match-beginning Возвращает позицию начала текста найденного последним выражением для регулярного поиска.
  • looking-at Возвращает t, если текст следующий за курсором сопоставим с аргументом, которое должно быть регулярным выражением.
  • eobp Возвращает t, если курсор находится в конце доступной области буфера. Конец доступной области буфера — это конец буфера, если не включено сужение, или конец суженной области, если сужение включено.
  • prog1 Последовательно вычисляет свои аргументы и возвращает значение аргумента вычисленного первым.

Тэги

M-! etags –help

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

Программа etags обрабатывает исходные тексты программ на языках Emacs Lisp, Common Lisp, Scheme, C, Fortran, Pascal, LaTeX, и большинство ассемблеров. При анализе файла программа etags автоматически узнает используемый в файле язык по расширению файла и его содержанию.

Файлы `TAGS' очень помогают тогда, когда вы сами работаете над большим программным проектом и хотите найти функцию расположеную в неизвестном вам файле. После создания новых функции все что вам надо — это снова запустить программу etags и новые функции будут занесены в таблицу тегов.

Сноски:

1

https://www.youtube.com/watch?v=MtVYwcuZ_E8 введение в макросы ООП в Emacs

Дата: 2019-02-21 Чт 23:37

Автор: valber

Created: 2019-08-26 Пн 00:06

Validate