Обновить поле структуры - PullRequest
       21

Обновить поле структуры

1 голос
/ 17 октября 2019

Есть ли более эргономичный способ применения функции к полю в структуре в Elisp?

Скажем, у меня есть следующее:

(cl-defstruct stack xs)

(defvar stack (make-stack :xs '(1 2 3 4 5)))

Есть ли простой способ применитьфункции в поле :xs. Мне бы хотелось, чтобы API был таким:

(update-field :xs stack (lambda (xs)
                          (cl-map 'list #'1+ '(1 2 3 4 5))))

Кто-нибудь знает, существует ли он?

Обновление: Я ищу способ СУШИТЬ вверхзвонки на (stack-xs stack) (см. ниже). То, что я ищу, больше похоже на что-то вроде Map.update от Elixir.

(setf (stack-xs stack) (cl-map 'list #'1+ (stack-xs stack)))

Ответы [ 2 ]

0 голосов
/ 13 ноября 2019

Я решил эту проблему, написав макрос, struct/update:

(defmacro struct/update (type field f xs)
  "Apply F to FIELD in XS, which is a struct of TYPE.
This is immutable."
  (let ((copier (->> type
                     symbol-name
                     (string/prepend "copy-")
                     intern))
        (accessor (->> field
                       symbol-name
                       (string/prepend (string/concat (symbol-name type) "-"))
                       intern)))
    `(let ((copy (,copier ,xs)))
       (setf (,accessor copy) (funcall ,f (,accessor copy)))
       copy)))

Я использую этот макрос следующим образом:

(defun set/add (x xs)
  "Add X to set XS."
  (struct/update set
                 xs
                 (lambda (table)
                   (let ((table-copy (ht-copy table)))
                     (ht-set table-copy x 10)
                     table-copy))
                 xs))

, который обновит следующую структуру:

(cl-defstruct set xs)
0 голосов
/ 17 октября 2019

Макрос cl-defstruct создает средства доступа к слотам в форме NAME-SLOT, где NAME - это имя типа структуры, а SLOT - это имя слота. Используя ваш пример, вы можете установить слот xs с помощью setf с помощью средства доступа к слоту, например:

(cl-defstruct stack xs)
(defvar st (make-stack :xs '(1 2 3 4 5)))
(setf (stack-xs st) (cl-map 'list #'1+ (stack-xs st)))
(stack-xs st)

В последней строке выше возвращается '(2 3 4 5 6).

Обновление: Недостатком вызова setf, показанного выше, является то, что средство доступа к слоту необходимо использовать дважды: один раз, чтобы прочитать текущее значение, а затем снова, чтобы обновить его до нового значения. Вы можете использовать cl-callf для СУШКИ:

(cl-callf (lambda (p) (cl-map 'list #'1+ p)) (stack-xs st))

В качестве альтернативы вы можете заключить вызов setf в новый метод, определенный для типа stack, используя cl-defmethod, возможно, так:

(cl-defmethod stack-update ((s stack) f slot)
  "Update SLOT in stack S by applying F.
F is passed one argument, the current value of SLOT,
and is expected to return a new value for SLOT."
  (let ((sl (if (keywordp slot) (intern (substring (symbol-name slot) 1)) slot)))
    (setf (cl-struct-slot-value 'stack sl s) (funcall f (cl-struct-slot-value 'stack sl s)))))

Затем вы можете позвонить stack-update следующим образом:

(stack-update st #'(lambda (p) (cl-map 'list #'1+ p)) :xs)

или эквивалентно:

(stack-update st (apply-partially 'cl-map 'list #'1+) 'xs)
...