Common Lisp: ненулевые аргументы и их имена для alist, как? - PullRequest
2 голосов
/ 12 апреля 2011

Я довольно новичок в Common Lisp и программировании, и я пытаюсь написать определенную функцию, которая превращает все не нулевые аргументы в alist.Пока я могу думать только о том, что:

(let ((temp nil))
    (if arg1
        (setf temp (acons 'arg1 arg1 nil)))
    (if arg2
        (setf temp (acons 'arg2 arg2 temp)))
    ...
    (if arg20-ish
        (setf temp (acons 'arg20-ish arg20-ish temp)))

    (do-something-with temp))

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

Самое сложное для меня - выяснить, как получить имена аргументов или какой символ использовать без ручного кодирования каждого случая.Если бы & rest предоставил имена arg, было бы легко отфильтровать NIL с помощью loop или mapcar, но, поскольку это не так, я не могу понять, как это «автоматизировать».
Я полностью заинтересован в других решениях, кромеодин описал, если люди думают, что это неестественно.

Редактировать : Ниже приведен пример того, что я пытаюсь сделать:

Объект создан с нефиксированным числом пар данных и некоторыми тегами,Например:

user = "someone"  
creation-time = (get-universal-time)  
color-of-sky = "blue"  
temperature-in-celsius = 32  
language = "Common Lisp"
...  
tags = '("one" "two" "three")

Эти свойства (т.е. имена ключей / аргументов) могут каждый раз отличаться.Новый объект будет добавлен в коллекцию;Я думал, что массив может работать хорошо, так как мне нужно постоянное время доступа и нужен только числовой идентификатор.
Коллекция будет содержать все больше и больше таких пользовательских объектов на неопределенный срок.
Я хочу иметь возможность быстрого доступа ко всем соответствующим объектамлюбая комбинация любых тегов, используемых в этих объектах.
Поскольку в массиве предполагается хранить все больше и больше данных в течение длительного периода, я не хочу анализировать каждый элемент в нем каждый раз, когда мне нужно искатьтег.Таким образом, я также храню индекс каждого объекта с данным тегом в хэш-таблице под именем тега.Я написал эту функцию, и мне трудно понять, как собирать данные и превращать их в список или что-то, что я могу легко проанализировать, проиндексировать и сохранить.

Ответы [ 2 ]

2 голосов
/ 13 апреля 2011

Этот макрос определит функцию, которая превращает свои ненулевые аргументы в alist , связанный во время выполнения тела:

(defmacro defnamed (fun-name alist-sym (&rest args) &body body)
  `(defun ,fun-name (,@args)
     (let ((,alist-sym))
      ,@(mapcar
         (lambda (s)
          `(when ,s
            (push (cons ',s ,s) ,alist-sym)))
         (reverse args))
      ,@body)))

Демонстрация:

(defnamed make-my alist (a b c) 
  alist)

(make-my 1 NIL 3)

=> ((A . 1) (C . 3))

2 голосов
/ 12 апреля 2011

Вот своего рода решение с использованием макросов:

(defmacro named-args (fun-name alist-sym (&rest syms) &body body)
  `(defun ,fun-name (&key ,@syms)
     (declare (special ,@syms))
     (let ((,alist-sym
            (loop
               for s in ',syms
               collecting (cons s (symbol-value s)))))
       ,@body)))

Затем вы можете использовать его с чем-то вроде

(named-args f u (a b c)
  (format t "~A~%" u))

, которое расширяется до

(DEFUN F (&KEY A B C)
  (DECLARE (SPECIAL A B C))
  (LET ((U
         (LOOP FOR S IN '(A B C)
            COLLECTING (CONS S (SYMBOL-VALUE S)))))
    (FORMAT T "~A~%" U)))

Наконецвызов даст

(f :a 3) => ((A . 3) (B) (C))

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

Снова глядя на ваш вопрос, похоже, что вы на самом деле не хотите аргументы ключевых слов, которые не были переданы.В этом случае вы можете проанализировать аргумент &rest (хотя это плоский список, поэтому вам нужно отобразить его вдоль по двое), или вы можете изменить макрос следующим образом:

(defmacro named-args (fun-name alist-sym (&rest syms) &body body)
  `(defun ,fun-name (&key ,@syms)
     (declare (special ,@syms))
     (let ((,alist-sym
            (loop
               for s in ',syms
               when (symbol-value s)
               collecting (cons s (symbol-value s)))))
       ,@body)))

, а затемВы получаете

(f :a 3) => ((A . 3))
...