Creación de una biblioteca de funciones para mutar variables globales arbitrarias

StackOverflow https://stackoverflow.com/questions/20353946

  •  25-08-2022
  •  | 
  •  

Pregunta

ACTUALIZACIÓN: Si tengo algunas funciones que quiero mutar una variable global que se pasa como argumento de una función, algunos pueden hacerlo pasando solo el símbolo de lo global (como en el caso de empujar algo a parte de una lista) , pero otras funciones requerirán que el símbolo se cita cuando se apruebe para que pueda acceder al valor del símbolo y mutar la variable externa, requerida para usar SETQ en una función. Esta pregunta es básicamente acerca de crear una interfaz consistente para un usuario de estas funciones sin confundirlas que a veces pases el símbolo, otras veces pasas el símbolo citado

Ejemplo de la forma de optimizar la interfaz para un montón de funciones, utilizando un ayudante que tiene el mismo uso sin requisitos de cómo funciona la función de mutación:

Aquí hay 2 ejemplos básicos de mi técnica:

(defun push-something (global something)
  (push something global)
  global) ;; not necessary to return it here since the 
          ;; preceding line already does, but doing so 
          ;; for consistency with other functions and clarity

(defun push-cdr (global something)
  (push something (cdr global))
  global) ;; must return it if I am going to set the 
          ;; dynamic var to the result of this function, 
          ;; because the preceding line doesn't return the 
          ;; entire new result of the variable

;;; "dirty" function for mutating global variable using "clean" functions above:

(defun set-global (fn global &rest values)
  (setf (symbol-value global) (apply fn (copy-list (eval global)) values)))

En este último, debo eval la global ya que se cita para su uso con symbol-value y luego yo copy-list de modo que global no está directamente mutado accidentalmente por lo que sea fn Elijo (como sería el caso con push-cdr que podría ser no trivial en funciones más complicadas).

Producción:

CL-USER> (defparameter *g2* nil)
*G2*
CL-USER> (set-global #'push-something '*g2* 5)
(5)
CL-USER> *g2*
(5)
CL-USER> (set-global #'push-cdr '*g2* 99)
(5 99)
CL-USER> *g2*
(5 99)

... y las mismas funciones podrían usarse con *g1* o *g3* etc en toda la función de acceso set-global.

¿Es este estilo bueno o malo, o me estoy perdiendo una manera mucho mejor de lograr esta funcionalidad?

¿Fue útil?

Solución 2

¿Por qué no te deshaces de set-global función por completo, y si desea que toda su función tenga el mismo estilo de pasar en la variable global, solo haga que todos usen symbol-value Por lo tanto, cualquiera que use estas funciones no necesitará saber cuál y cuál no requiere una cotización frente al nombre de la variable global que se pasa. Mucho más fácil, ¿verdad?

Otros consejos

Este es un mal estilo y una pérdida de tiempo.

  1. push-something es equivalente a cons.

  2. push-cdr es no funcional: modifica el argumento en el lugar o falla con un error en nil argumento.

Si desea cambiar el valor de la variable global, entonces utilizando setf Explícitamente es mucho más claro.

Si desea modificar las partes internas de un valor global de alguna manera compleja, es posible que deba escribir una función especial para eso, pero esa función debe ser agnóstica del hecho de que está modificando el valor vinculado a una variable global en lugar de una variable léxica.

En resumen, hay dos problemas ortogonales separados: cambiar una variable global (use setq) y cambiar las partes internas de un objeto (sea agnóstico de su naturaleza global).

PD. No estoy seguro de lo que quiere decir con "SETF no cambia el valor de una variable global cuando está dentro de la función", pero::

(defvar *foo* 10)
*FOO* ==> 10
(defun change-foo (x) (setq *foo* x))
(change-foo 123)
*foo* ==> 123
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top