LINUX.ORG.RU

макросы и обработка списка структур

 ,


0

2

Привет.

Хочу решит такую задачу:

есть

(defstruct sensordata date press velo volume) ;в реальной задаче 24 слота, но я оставил 4 для удобства чтения
(defparameter  *test-data* (list (make-sensordata 
				  :date   "8575" ;для моего вопроса совершенно не важно, дата здесь или нет
				  :press   (read-from-string "1")
				  :velo  (read-from-string "1")
				  :volume (read-from-string "1"))
				 (make-sensordata 
				  :date   "345345"
				  :press   (read-from-string "2")
				  :velo  (read-from-string "2")
				  :volume (read-from-string "2"))
				 (make-sensordata 
				  :date   "345345"
				  :press   (read-from-string "3")
				  :velo  (read-from-string "3")
				  :volume (read-from-string "3"))
				 ))

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

(mapcar #'sensordata-press *test-data*)
в этих выражениях меняются оператор отображения и названия слотов.

Писать кучу однотипных функций не очень хочется, потому я попробовал написать макрос (пока что «в лоб», без различных проверок):

(defmacro map-function-on-structures-slot (function-name structure-name structure-slot-name list-of-structures)
  (let ((slot-access-name (concatenate 'string (symbol-name structure-name) "-"  (symbol-name structure-slot-name))))
    `(,function-name `(function ,(intern ,slot-access-name)) ,list-of-structures)))

в результате вычисления

(map-function-on-structures-slot mapcar sensordata velo *test-data*)
я получаю
#'SENSORDATA-VELO is not of type (OR
                                  SYMBOL
                                  FUNCTION), and can't be FUNCALLed or APPLYed
   [Condition of type TYPE-ERROR]

Вопросы:

  • как я понимаю, я меня типичная для «непонимающих» макросы ошибка. В чем она заключается и как её исправить?
  • на сколько уместно выбрана структура данных (список состоящий из структур) для данной задачи (стат. обработка данных снятых с датчиков в единицу времени)?

Спасибо.

> как я понимаю, я меня типичная для «непонимающих» макросы ошибка

К макросам она, вообще говоря, не относится.

> В чем она заключается и как её исправить?

Неправильная конструкция:

`(function ,(intern ,slot-access-name))

Вместо этого надо так:

',(intern slot-access-name)

> Насколько уместно использовать здесь макросы (не заданный вопрос)

На кой черт вообще нужен это макрос? Ну если так хочется, то сделай простую функцию:

(defun map-function-on-structure-slot (handler slot-accessor data)
  (funcall handler slot-accessor data))
(map-function-on-structure-slot #'map #'sensordata-velo *test-data*)

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

archimag ★★★
()
Ответ на: комментарий от archimag

Или если очень хочется работать именно через имя слота, то так:

(defun map-function-on-structure-slot (handler slot data)
  (flet ((slot-accessor (object)
           (slot-value object slot)))
    (funcall handler #'slot-accessor data)))

(map-function-on-structure-slot #'mapcar 'velo *test-data*)
archimag ★★★
()

на сколько уместно выбрана структура данных (список состоящий из структур) для данной задачи (стат. обработка данных снятых с датчиков в единицу времени)?

У структуры можно поля типизировать:

(defstruct sensordata
  (date nil :type unsigned-byte) ;; (get-universal-time) возвращает unsigned-byte
  (press nil :type bit)          ;; bit = {0, 1}, как пример
  (velo nil :type (integer 0 8)) ;; (integer 0 8) = {0, 1, 2, 3, 4, 5, 6, 7, 8}, тоже пример
  (volume nil :type fixnum))     ;; fixnum это целое фиксированной битности (например, 29 бит на x86 и SBCL)
  ;; Создать структуру не задав какое-то поле нельзя, т.к. nil имеет тип null несовместимый с заданными.
  ;; Создать структуру с типами полей несовместимыми с заданными тоже нельзя.

если нужно часто добавлять новые структуры и обходить получившуюся последовательность, то списки вполне подходят - push для добавления за O(1) (новые данные будут в начале списка, более поздние - дальше), map* для обхода за O(n), но доступ к элементу - тоже O(n). Можно ещё попробовать взять adjustable array из структур - обход за те же O(n), доступ к элементу уже за O(1) (предпочтительно, если с собранными данными нужно делать что-то нелокальное, т.е. смотреть не только на текущий/соседние элементы при линейном обходе), добавление может быть более затратным, но не на много (это зависит от реализации, если реалокация массива будет производиться каждые 2^n, то добавление будет быстрым, но за счёт более активного использования памяти).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Можно ещё попробовать взять adjustable array из структур

И можно попробовать взять хэш-таблицу unsigned-byte -> структура со снятыми данными.

quasimoto ★★★★
()

спасибо всем за ответы, вечер буду разбираться.

kvitaliy
() автор топика
Ответ на: комментарий от quasimoto

quasimoto, отдельная благодарность за строки про типизацию.

Что касается информации про порядки роста, то в каком источнике это все можно найти самому, что бы каждый раз не спрашивать на форуме? И, спасибо за информацию.

kvitaliy
() автор топика
Ответ на: комментарий от archimag

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

Спасибо.

kvitaliy
() автор топика
Ответ на: комментарий от kvitaliy

то в каком источнике это все можно найти самому

То о чём я сказал - вот, например. А вообще - Cormen et al. Introduction to Algorithms.

В лиспе списки кодируются cons-ами:

struct cons {
    lispobj car;
    lispobj cdr;
};

добавление уже созданного объекта в начало требует просто обновления car (O(1)). Поиск элемента в списке - следования по cdr (O(n)).

Относительно adjustable массивов я делаю допущения, что они работают как динамические массивы, хотя это всё на усмотрение реализации. Для динамических массивов добавление в конец тоже O(1), но требует реаллокаций время от времени:

    if (new_length > array->length) {
        new_length = calculate_next_length(new_length);
        array->elements = realloc(array->elements, new_length);
        array->length = new_length;
    }

calculate_next_length может расти по линейному закону, либо по экспоненте (больше памяти, меньше реаллокаций), либо ещё как-то.

В конце концов, можно комбинировать то и другое - делать связные списки массивов фиксированного размера (тогда не будет необходимости в реаллокациях).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

обновления car

В смысле:

cons *new_list = allocate new cons
new_list->car = new_element
new_list->cdr = old_list
quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

спасибо за ссылку и понятные пояснения.

kvitaliy
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.