Расширения setf Common Lisp для функций, которые обращаются к битам целых чисел - PullRequest
2 голосов
/ 27 мая 2020

Я пишу программу на Common Lisp, которая должна хранить кучу битов состояния для очень большого количества записей в массиве (вся программа в значительной степени fortran-in-lisp), а биты состояния закодированы как биты фиксированного числа, находящиеся в этом массиве. Аксессоры для этих битов состояния на самом деле будут определяться макросом, поэтому мне не нужно заботиться о распределении битов, но функция считывателя образцов может быть

(defun deadp (e)
  (logbitp 0 e))

(в реальной жизни это будет быть встроенными и загроможденными объявлениями, чтобы попытаться убедиться, что это быстро, но я думаю, здесь это не имеет значения.)

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

Тогда я буду использовать это как-то так:

(defconstant status-index 3)
...
(dotimes (i nentries)
  (unless (deadp (aref entries i status-index))
    ...))

(На самом деле life (aref entries i status-index) будет (status entries i), который, в свою очередь, потребует метода setf, но я думаю, что это легко.)

или

(loop for i below nentries
      counting (if (deadp entries i status-index) 1 0))

И, конечно, будет быть другими аналогичными однобитными флагами, которые будут иметь разные биты, связанные с ними.

Итак, теперь я хочу иметь возможность сделать это:

(dotimes (i nentries)
  ...
  (when ...
    (setf (deadp (aref entries i status-index) t)))
  ...)

, который должен превратиться в код, эквивалентный

(dotimes (i nentries)
  ...
  (when ...
    (progn 
      (setf (ldb (byte 1 0) (aref entries i status-index)) 1)
      t))
  ...)

А также это:

(let ((status 0))
  ...
  (when ...
    (setf (deadp status) t))
  ...)

который ш может превратиться в код, эквивалентный этому:

(let ((status 0))
  ...
  (when ...
    (progn
      (setf (ldb (byte 1 0) status) 1)
      t))
  ...)

Другими словами, я хочу, чтобы моя функция deadp была аксессором, а setf на ней работала в общем для нее: (setf (deadp (cdr x)) nil) должно работать, et c.

Итак, это сбило меня с толку в части CL, которую я избегал долгое время: определение расширителей setf. Совершенно очевидно, что простое определение функции (setf deadp) не будет работать, потому что числа неизменны, и я достаточно уверен, что defsetf недостаточно мощный, поэтому мне нужно define-setf-expander, чего я не делаю. Не понимаю.

Может кто-нибудь объяснить, как мне это нужно делать? Я думаю, что конкретная функция deadp не критична, хотя все функции, которые меня волнуют, будут выглядеть как ее варианты.


Альтернативный ответ был бы «это безумный подход, вместо этого ... . ', и я открыт для них. Я подумал о написании кода, который абстрагирует массив, поэтому вместо (deadp (aref ...)) я бы написал (deadp people ...), где people - это массив людей. Это было бы хорошо, и легко увидеть, как сделать это setf способным, за исключением того, что я также хочу иметь возможность сказать (deadp status), где status - это просто фиксированное число. Но, возможно, есть подход получше.

1 Ответ

3 голосов
/ 27 мая 2020

Согласно документации SBCL для GET-SETF-EXPANSION, расширитель setf должен: «Возвращать пять значений, необходимых для оборудования SETF: список временных переменных, список значений для их заполнения, список временных переменных. для новых значений, функции настройки и функции доступа ». Функция настройки и функция доступа на самом деле являются просто формами, которые устанавливают и получают доступ к значению на месте, а не объектами функций.

Попробуйте следующее:

(define-setf-expander deadp (place)
  (let ((new (gensym)))
    (values nil nil (list new)
            `(progn (setf (ldb (byte 1 0) ,place) (if ,new 1 0))
                    ,new)
            `(deadp ,place))))

Пример расширения:

(let ((status 1))
  (setf (deadp status) t))
->
(let ((status 1))
  (LET* ((#:G605 T))
    (SETF (LDB (BYTE 1 0) STATUS)
            (IF #:G605
                1
                0))
    #:G605))
...