2012-09-17 11 views
5

Tengo una clase con varias ranuras. También tengo una función de constructor para hacer objetos de esa clase de tal manera que pasar la siguiente lista '(:id "john" :name "John Doe" :age 42) a esa función construirá un nuevo objeto con esos valores de espacios. Usaré esa función para generar más de un objeto, usando una lista de listas.¿Cómo puedo convertir una palabra clave en un símbolo adecuado para acceder a una ranura?

¿Cómo puedo convertir desde una palabra clave como :id en un nombre de ranura que SLOT-VALUE puede usar?

Gracias.

Respuesta

3

Las funciones find-symbol y symbol-name le serán útiles. Si defclass y slot-value suceda en el mismo paquete, puede utilizar las funciones de la siguiente manera:

(defclass person() 
    ((id :initarg :id) 
    (name :initarg :name) 
    (age :initarg :age))) 

(slot-value (make-instance 'person :id "john" :name "John Doe" :age 42) 
      (find-symbol (symbol-name :id))) 

Si defclass y slot-value suceda en dos paquetes diferentes, es necesario dar find-symbol el nombre del paquete, donde sucede defclass:

(in-package #:common-lisp-user) 

(defpackage #:foo 
    (:use #:common-lisp) 
    (:export #:person)) 

(defpackage #:bar 
    (:use #:common-lisp #:foo)) 

(in-package #:foo) 

(defclass person() 
    ((id :initarg :id) 
    (name :initarg :name) 
    (age :initarg :age))) 

(in-package #:bar) 

(slot-value (make-instance 'person :id "john" :name "John Doe" :age 42) 
      (find-symbol (symbol-name :id) 'foo)) 

(find-symbol name &optional (package (sane-package)))

Función: Devuelve el símbolo denominado STRING en PAQUETE. Si se encuentra dicho símbolo, entonces el segundo valor es: INTERNO,: EXTERNO o: HEREDADO para indicar cómo se puede acceder al símbolo. Si no se encuentra ningún símbolo, ambos valores son NIL.

(symbol-name symbol)

Función: el nombre de Retorno símbolo como una cadena.

+0

Gran respuesta. Gracias. – sbenitezb

10

Si las palabras clave son las initargs de la clase, a continuación, puedes llamar MAKE-INSTANCE través APPLY:

(defclass person() 
    ((id :initarg :id ) 
    (name :initarg :name) 
    (age :initarg :age))) 


CL-USER > (mapcar 
      (lambda (initargs) 
      (apply #'make-instance 'person initargs)) 
      '((:id "john" :name "John Doe" :age 42) 
      (:id "mary" :name "Mary Doe" :age 42))) 

(#<PERSON 402027AB7B> #<PERSON 402027AC33>) 
+0

Me gusta su enfoque "más funcional". – sbenitezb

+3

@WhiteCat ¿Fue su pregunta sobre cómo llamar 'make-instance' con esas listas? Entendí su pregunta de manera diferente porque el título y el cuerpo de la pregunta dicen "¿Cómo puedo convertir una palabra clave en un símbolo adecuado para acceder a una ranura?" y "¿Cómo puedo convertir desde una palabra clave como: id a un nombre de ranura que' SLOT-VALUE 'puede usar? ". Si su verdadero objetivo es simplemente llamar a 'make-instance', no' slot-value', la solución de Rainer Joswig es un camino por recorrer. – dkim

+2

@dkim: mi pregunta fue exactamente como fue escrita, la cual respondió correctamente. Lo había intentado con '(find-symbol ...)' antes, pero usé KEYWORD como paquete, por lo que no funcionó. La respuesta de Rainer me dio solo otra perspectiva, una que no consideré y que es la solución correcta. Gracias a ambos. – sbenitezb

-1

Mi solución a esta estupidez de CL fue:

(defun locate-symbol 
     (inst kw) 
    (let* ((slot-name (symbol-name kw)) 
     (slot-def (find slot-name 
         (clos:compute-slots (class-of inst)) 
         :test #'(lambda (name sd) 
            (string= name 
              (symbol-name (clos:slot-definition-name sd))))))) 
    (if slot-def 
     (clos:slot-definition-name slot-def) 
     (error "Can't find a slot definition named ~s." slot-name)))) 

(defun gets 
     (self slot-name) 
    "Get a value of a slot by its name (keyword)" 
    (slot-value self (locate-symbol self slot-name))) 

(defun sets! 
     (self slot-name value) 
    "Set a value of a slot by its name (keyword)" 
    (setf (slot-value self (locate-symbol self slot-name)) 
     value)) 

Así que ahora usted puede hacer:

(defvar obj (make-instance '<some-class>)) 
(sets! obj :some-slot "some value") 
(format t "-> ~a~%" (gets obj :some-slot)) 
1

Soy consciente de que esto es bastante antiguo, pero creo que el punto más importante que debe hacerse aquí es:

No utilice slot-value así!

el fin de obtener un descriptor de acceso, utilice los :accessor o :reader opciones de ranura, y para pasar valores al constructor, utilice :initarg:

(defclass foo() 
    ((bar :accessor foo-bar :initarg :bar))) 

Esto significa: crear un método getter y un expansor setf llamado foo-bar, y use un argumento de palabra clave llamado :bar en make-instance para inicializar el valor de este intervalo.

Ahora usted puede crear una instancia de un objeto tal como esto:

(make-instance 'foo :bar "quux") 

o, si se obtiene una lista de propiedades de initargs (como Rainer ya había mostrado):

(let ((initargs (list :bar "quux"))) ; getting this from somewhere 
    (apply #'make-instance 'foo initargs)) 

entonces se puede obtener el valor de la siguiente manera:

(foo-bar some-foo) 

y configurarlo con setf como de costumbre:

(setf (foo-bar some-foo) "wobble") 

Si utiliza :reader en lugar de :accessor, no está permitido ajuste. Esto a menudo es útil para comunicar el intento de inmutabilidad.

Slot-value es realmente para situaciones especiales en la vida de un objeto, como cuando se juega con los métodos para initialize-instance. Ese es un tema avanzado.

Cuestiones relacionadas