Comment puis-je faire setf sur les accesseurs d'une structure lors de l'utilisation d'un stagiaire
-
30-09-2019 - |
Question
Je voudrais setf
différents champs d'une structure en fonction d'une certaine variable.J'ai décidé d'utiliser l'approche suivante :
Générez une chaîne avec le nom de l'accesseur du champ :
(setq my-string (format nil "STRUCT-ESTADISTICAS-NUM-~S" x))
puis utilisez stagiaire avec funcall :
(funcall (intern my-string) *estadisticas*)
Cet appel renvoie la valeur correcte du champ de la structure, mais si j'essaye setf
pour modifier cette valeur, il se plaint en disant :
(setf (funcall(intern my-string) *estadisticas*) 0)
Error: `(SETF FUNCALL)' is not fbound
Je peux comprendre pourquoi cela ne fonctionne pas, mais je ne trouve pas de moyen de modifier les champs de la structure.Une idée?Merci.
La solution
Vous voulez appeler une fonction d'auteur de la struct par son nom, et le nom de l'écrivain est la liste (setf accessor-name)
; si
(funcall (fdefinition (list 'setf (intern my-string))) 0 estadisticas)
Edit:
Ne pas voir le reste de votre code, il est difficile de comprendre ce qui a mal tourné. Sur SBCL cela fonctionne pour moi:
(defstruct point x y)
(let ((point (make-point :x 1 :y 2)))
(funcall (fdefinition (list 'setf (intern "POINT-X"))) 10 point)
point)
Les évalue ci-dessus à
#S(POINT :X 10 :Y 2),
comme prévu.
Autres conseils
Motivation:
Les structures sont des installations de niveau relativement bas.La « vitesse » était un objectif de conception important.L'indirection via les fonctions d'écriture n'est pas prise en charge par la norme (comme je l'ai lu).Aujourd'hui, utilisez CLOS par défaut, à moins que l'on ait besoin d'une meilleure efficacité des structures (une lecture et une écriture plus rapides des slots avec des structures sont parfois possibles).
Premier style :
N'utilisez pas INTERN, utilisez FIND-SYMBOL.Spécifiez également le package, sinon FIND-SYMBOL utilisera la valeur d'exécution de *package* comme package
Deuxièmement - DÉFSTRUCTION
Si j'ai lu correctement la norme ANSI CL, ce n'est pas que DEFSTRUCT crée des fonctions d'écriture pour les emplacements comme le fait DEFCLASS.
CL-USER 24 > (defstruct foo bar baz)
FOO
CL-USER 25 > #'(setf foo-bar)
Error: Undefined function (SETF FOO-BAR) in form (FUNCTION (SETF FOO-BAR)).
Ainsi, construire un tel nom (SETF FOO-BAR) et essayer de trouver une fonction pour cela échouera, car aucune fonction de ce type n'est définie par DEFSTRUCT.
Cela fonctionne dans le code utilisateur (setf (foo-bar some-struct) 42), est basé sur les extensions SETF définies fournies par DEFSTRUCT, mais pas sur les fonctions d'accès SETF définies.
Certaines implémentations de Common Lisp peuvent fournir des fonctions d'écriture en tant qu'extension non standard d'ANSI CL.
Solutions possibles:
a) utilisez les classes CLOS, DEFCLASS fait ce que vous voulez
b) écrivez vous-même les fonctions d'écriture
(defun (setf foo-bar) (new-value struct)
(setf (foo-bar struct) new-value))
Maintenant:
(funcall (fdefinition '(setf foo-bar)) 300 *foo*)
Ci-dessus fonctionne alors.
c) (SETF SLOT-VALUE) - une autre fonctionnalité non standard de certaines implémentations.
Dans certaines implémentations de Common Lisp, cela fonctionne non seulement pour les classes CLOS, mais aussi pour les structures :
(setf (slot-value some-struct 'bar) 42)
Je ne sais pas si Allegro CL prend en charge cela, mais ce serait facile à découvrir.