Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
ÍNDICE DE CONTENIDOS
INTRODUCCIÓN
PRIMERA PARTE:
EL ENTORNO DE DESARROLLO VISUAL LISP
1. VISUAL LISP
2. EL ENTORNO DE DESARROLLO VISUAL LISP
o El Trabajo con Visual LISP y AutoCAD
o Barra de Menús
o Las Barras de Herramientas
o La Consola Visual LISP
o El Editor Visual LISP
Barras de Herramientas
Menú Contextual
Teclas Rápidas
SEGUNDA PARTE:
TÉCNICAS FUNDAMENTALES DE LA PROGRAMACIÓN LISP
1. TIPOS DE DATOS
o ÁTOMOS
o ÁTOMOS SIMBÓLICOS (S-ATOMS)
o CONSTANTES
NÚMEROS
CADENAS DE TEXTO
o LISTAS Y CONSES
LISTAS DE UN NIVEL
LISTAS ANIDADAS
LISTAS DE ASOCIACIÓN (A-LIST)
LA LISTA VACÍA (NIL)
o FUNCIÓN TYPE: EXAMEN DEL TIPO DE DATO
TIPOS DE DATOS LISP
TIPOS DE DATOS AUTOCAD
TIPOS DE DATOS ACTIVE-X
2. FUNCIONES
o FUNCIONES PRIMITIVAS
OPERADORES ARITMÉTICOS
FUNCIONES DE ACCESO A LISTAS
CONSTRUCCIÓN DE LISTAS
PROCESAMIENTO DE LISTAS
TRATAMIENTO DE CADENAS
TRATAMIENTO DE CADENAS CON VLISP
o FORMAS ESPECIALES
o FUNCIONES DEFINIDAS POR EL USUARIO
DEFUN: NUEVAS FUNCIONES PARA LA
EXTENSIÓN DE LISP
REALES A ENTEROS: TRUCAMIENTO O
REDONDEO
FUNCIONES TRIGONOMÉTRICAS
LAMBDA
FUNCTION
LOAD
3. ESTRUCTURAS DE CONTROL
o PREDICADOS GENERALES
o PREDICADOS ARITMÉTICOS
o OPERADORES LÓGICOS
o OPERADORES LÓGICOS BINARIOS
Números Binarios
Función LSH
Función ~ (NOT lógico binario)
Función LOGAND
CONVERSIÓN ENTRE BINARIOS Y
DECIMALES (I)
CONVERSIÓN ENTRE BINARIOS Y
DECIMALES (II)
Función LOGIOR
Función BOOLE
o PREDICADOS DEFINIDOS POR EL USUARIO
o ESTRUCTURAS CONDICIONALES
4. FUNCIONES RECURSIVAS E ITERATIVAS
o FUNCIONES RECURSIVAS
EXTRACCIÓN DE LOS VÉRTICES DE UNA
POLILÍNEA
o FUNCIONES ITERATIVAS
REPETICIÓN UN NÚMERO DETERMINADO
DE VECES
ITERACIONES SOBRE ELEMENTOS DE UNA
SECUENCIA
CICLOS DEPENDIENTES DE UNA
CONDICIONAL
o FUNCIONES DE MAPEADO SOBRE SECUENCIAS
EXTRACCIÓN DE LOS VÉRTICES DE UNA
POLILÍNEA
Una solución más eficaz.
o ¿CUÁNDO RECURSIÓN Y CUÁNDO ITERACIÓN?
TERCERA PARTE:
ACCESO A LA BASE DE DATOS GEOMÉTRICA
La Ventana de la Aplicación
Barra de Menús
Asumimos que el lector está familiarizado con el uso de menús
desplegables en AutoCAD u otras aplicaciones. Sólo cabría
destacar que éstos menús son sensibles al contexto en que se
utilizan. Es decir, que algunas opciones que pueden estar
activas si se abre la ventana del Editor pueden no estarlas si el
foco se encuentra en la Consola o en la ventana de TRACE.
FILE
Creación de nuevos ficheros de programas abiertos para
su edición
Apertura de ficheros existentes
Guardar los cambios efectuados
Compilar aplicaciones Visual LISP
Imprimir los ficheros de programas
EDIT
Copiar y Pegar texto
Deshacer el último cambio en el Editor o la última función
ejecutada desde la Consola
Seleccionar texto en el Editor o la Consola
Comprobar el cierre de paréntesis
Recuperar funciones ejecutadas desde la Consola
SEARCH
Buscar y Reemplazar texto
Colocar marcadores de referencia (bookmarks) y navegar
en el texto utilizando estos marcadores.
VIEW
Buscar y mostrar el valor de variables y símbolos en su
código AutoLISP.
PROJECT
Trabaja con los proyectos y la compilación de programas
DEBUG
Establecer y quitar puntos de ruptura en los programas
Ejecutar programas paso a paso comprobando el estado
de las variables y el valor devuelto por las expresiones.
TOOLS
Establecer las opciones para el formateado del texto y
varias otras opciones del entorno, tales como la ubicación
de ventanas y barras de herramientas.
WINDOW
Organiza, abre y cierra ventanas en el IDE.
HELP
Pues eso, la Ayuda en línea.
Barras de Herramientas
Menú Contextual
Teclas Rápidas
Copias de Seguridad
El Editor VLISP viene configurado para
hacer siempre copias de seguridad de
los ficheros. Estas copias de seguridad
se sitúan en el mismo directorio de la
fuente del programa usando el mismo
nombre y la extensión _ls. De esta
manera la copia de seguridad del fichero
caras.lsp se llamará caras._ls.
Para recuperar la última versión
guardada de un fichero se emplea la
opción Revert del menú Files.
Barras de Herramientas
Herramientas de
Edición
Búsqueda y
Marcadores
Revisión y
Formateo
Acceso a otras
Ventanas de la
Aplicación
Utilidades de
Depuración
Herramientas de Edición
La característica más
interesante de la
herramienta de
búsqueda del Editor
VLISP es su capacidad
de buscar en un grupo
de ficheros
seleccionados y mejor
aún, en los ficheros que
configuran un proyecto.
El resultado de la
búsqueda se muestra
en una ventana de
salida, llamada <Find
Output>, donde
aparecen completas las
expresiones donde se
emplea el término
buscado. Se puede
configurar la
búsqueda para que
inserte marcadores
(bookmarks) cada vez
que encuentre el
término buscado, de
Buscar y Reemplazar manera automática.
Para ello
Permite buscar y reemplazar texto en la ventana se deberá
activa del Editor. Se
marcar la casilla Mark
accede a esta opción desde el menú Search>Replace... (Teclas <CTRL>
Instances (Marcar
+ <H>). Instancias)
Memorización de búsquedas anteriores
Los términos utilizados en búsquedas anteriores se guardan en una lista
desplegable para su reutilización.
Inserción de Marcadores
Los marcadores permiten desplazarse dentro del texto de marcador en
marcador. Disponible en el menú Search>Bookmarks>Toggle Bookmark.
Hay problemas con los marcadores al usar la opción de formateo
automático del texto. (ver nota más abajo).
Autocad
Otras Ventanasde Visual LISP, incluyendo las de otros ficheros abiertos
para su edición.
La Consola Visual LISP
La ventana de Inspección de objetos LISP y AutoCAD.
El Trace Stack, que guarda la memoria de pila de errores.
El Symbol Service para la gestión de Símbolos.
La Utilidad de Apropos.
La Utilidad de Watch.
Utilidades de Depuración
Comandos de ARCHIVO:
<CTRL> + <S>
Guarda el contenido de la ventana activa del Editor.
<CTRL> + <ALT> + <S>
Guarda el contenido de la ventana activa del Editor con
otro nombre de archivo.
<CTRL> + <SHIFT> + <S>
Guarda el contenido de todas las ventanas de Editor
abiertas.
<CTRL> + <F4>
Cierra la ventana de Editor activa.
<CTRL> + <P>
Imprime el contenido de la ventana de Editor activa.
Comandos de Edición:
<CTRL> + <Z>
Deshacer la última operación en el Editor. Soporta ilimitadas
operaciones de Deshacer, hasta la última ocasión en que se
guardó el contenido del Editor a disco. Una vez guardado es
imposible deshacer. En ese caso, si fuera necesario, habría que
recuperar la copia de seguridad mediante la opción Files>Revert.
<CTRL> + <V>
Pegar el contenido del Portapapeles en el lugar señalado por el
cursor.
<SUPR>
Borrar el Texto señalado o el carácter a la derecha del cursor.
<CTRL> + <A>
Seleccionar todo el contenido de la ventana del Editor.
<CTRL> + <M>
Abre un menú desplegable con las opciones de búsqueda del
cierre de paréntesis.
<CTRL> + <)>
Desplaza el cursor hasta el paréntesis de cierre correspondiente.
<CTRL> + <(>
Desplaza el cursor hasta el paréntesis de apertura
correspondiente.
<CTRL> + <SHIFT> + <)>
Selecciona la expresión hasta el paréntesis de cierre
corresondiente, resaltándola en vídeo inverso.
<CTRL> + <SHIFT> + <(>
Selecciona la expresión hasta el paréntesis de apertura
corresondiente, resaltándola en vídeo inverso.
<CTRL> + <E>
Abre un menú desplegable con una serie de comandos
adicionales:
Identar Bloque de Texto.
Suprimir la Identación de un
bloque de Texto.
Identar hasta el nivel actual
Añadir un prefijo a cada línea de
texto seleccionada.
Añadir texto al final de cada línea
seleccionada.
Insertar Fecha.
Insertar Hora.
Definir el formato de fecha y
hora.
Comandos de Búsqueda:
<CTRL> + <F>
Buscar texto.
<CTRL> + <H>
Buscar y sustituir texto.
<CTRL> + <BARRA ESPACIADORA>
Completar una palabra por similitud a otras existentes en
la ventana actual del Editor. Cuando ya se ha tecleado un
nombre anteriormente, El editor VLISP facilita el
incorporarlo de nuevo al documento con sólo teclear los
caracteres iniciales. Si en el ejemplo de arriba
quisiéramos teclear de nuevo el nombre de la función
PosVert bastaría teclear Po y después pulsar <CTRL> +
<BARRA ESPACIADORA>. Si en lugar de encontrar
PosVert encontrara el nombre de variable pos, bastaría
con volver a pulsar <CTRL> + <BARRA ESPACIADORA>
cuantas veces fuera necesaro para llegar al texto
deseado.
<CTRL> + <SHIFT> + <BARRA ESPACIADORA>
Completar una palabra mediante APROPOS (buscando en
todo el conjunto de símbolos AutoLISP.
<ALT> + <.>
Poner o quitar marcador.
<CTRL> + <.>
Desplazarse al próximo marcador.
<CTRL> + <,>
Desplazarse al marcador anterior.
<CTRL> + <SHIFT> + <.>
Seleccionar hasta el próximo marcador.
<CTRL> + <SHIFT> + <,>
Seleccionar hasta el marcador anterior.
Comandos de Vistas:
<CTRL> + <SHIFT> + <I>
Abre una ventana de Inspección para introducir una
expresión.
<CTRL> + <SHIFT> + <T>
Abre la ventana de Inspección de Trace.
<CTRL> + <SHIFT> + <R>
Abre la ventana de Inspección para la última pila de
error.
<CTRL> + <SHIFT> + <S>
Abre una ventana de Inspección para un símbolo.
<CTRL> + <SHIFT> + <W>
Abre la ventana de Vigilancia (WATCH).
<CTRL> + <SHIFT> + <A>
Abre la ventana APROPOS para ayuda con expresiones
LISP.
<CTRL> + <SHIFT> + <B>
Muestra los puntos de interrupción actuales (en todos los
programas abiertos, no sólo la ventana activa).
<F6>
Abre la Consola Visual LISP y/o la pone en primer plano.
Comandos del Proyecto:
<CTRL> + <SHIFT> + <P>
Abre un proyecto existente.
Comandos de Depuración:
<CTRL> + <W>
Añadir una expresión a la ventana de Vigilancia (WATCH).
<F9>
Añade o suprime un punto de interrupción en la posición
actual del cursor.
<CTRL> + <SHIFT> + <F9>
Suprime todos los puntos de interrupción.
<CTRL> + <F9>
Busca y resalta el código que dio origen a la última
interrupción.
Herramientas de Desarrollo:
<CTRL> + <SHIFT> + <E>
Carga y evalúa el código seleccionado.
<CTRL> + <ALT> + <E>
Carga y evalúa el código contenido en la ventana activa
del Editor.
<CTRL> + <SHIFT> + <C>
Comprueba la sintaxis de las expresiones seleccionadas.
<CTRL> + <ALT> + <C>
Comprueba la sintaxis del código contenido en la ventana
activa del Editor.
<CTRL> + <SHIFT> + <F>
Formatea las expresiones seleccionadas.
<CTRL> + <ALT> + <F>
Formatea el código contenido en la ventana activa del
Editor.
Controles de Ventanas:
<ALT> + <F6>
Ajusta la ventana activa al espacio disponible en pantalla.
Comprobación del cierre de paréntesis
Las combinaciones de teclas <CTRL> + <(> y <CTRL> +
<)> permiten localizar el paréntesis de apertura o el
paréntesis de cierre respectivamente que corresponda a
partir de la posición donde se sitúa el cursor. Si se pulsa
simultáneamente <SHIFT> el texto quedará seleccionado
(en vídeo inverso).
Completar Texto
Cuando ya se ha tecleado un nombre anteriormente, El
editor VLISP facilita el incorporarlo de nuevo al
documento con sólo teclear los caracteres iniciales. Si en
el ejemplo de arriba quisiéramos teclear de nuevo el
nombre de la función PosVert bastaría teclear Po y
después pulsar <CTRL> + <BARRA ESPACIADORA>. Si
en lugar de encontrar PosVert encontrara el nombre de
variable pos, bastaría con volver a pulsar <CTRL> +
<BARRA ESPACIADORA> cuantas veces fuera necesaro
para llegar al texto deseado.
TIPOS DE DATOS
Ejemplos:
ÁTOMOS LISTAS
a ()
juan (juan)
45 (a juan 45 z5mn)
((juan 45) a
z5mn
((z5mn)))
Tanto los átomos como las listas son expresiones válidas LISP
que el intérprete lee y evalúa. Las reglas para la evaluación de
ambos tipos de objetos se describen a continuación:
ÁTOMOS
Los átomos son las expresiones LISP más elementales.
Siempre tienen un nombre constituido por una secuencia
de caracteres y por ello se asemejan a las palabras de un
lenguaje. Los nombres de átomos se separan de otras
expresiones mediante espacios en blanco, paréntesis o
cambios de línea. Un átomo no es divisible. Como norma
general, todos los elementos que no sean una lista se
consideran átomos. Al recibir un átomo, el evaluador lisp
intenta determinar su valor. Este valor puede estar
representado por el nombre mismo del átomo, que
entonces consideraremos como una "constante" o puede
ser un objeto LISP distinto, en cuyo caso estaremos ante
un átomo "simbólico". Los átomos simbólicos se utilizan
dentro de los programas para almacenar valores, siendo
conocidos entonces como "variables".
LISTAS:
El nombre LISP viene de LISt Processing (Procesamiento
de Listas), indicando el mecanismo fundamental sobre el
que se desarrolla el lenguaje. Las listas LISP son grupos
de valores relacionados, separados por espacios e
incluidos entre paréntesis redondos "(" y ")". En LISP, el
orden de los elementos incluidos en una lista es
significativo. En esto se distingue el concepto de lista del
concepto de conjunto, en el que el ordenamiento de los
términos no tiene relevancia. Otra diferencia entre listas
y conjuntos reside en que en una lista pueden haber
términos repetidos, mientras que en un conjunto cada
término ocurre una sóla vez. Las listas proporcionan un
método eficaz para almacenar valores relacionados.
AutoCAD expresa los puntos 3D como una lista de tres
números reales donde el primer valor es el de la
coordenada X, el segundo la coordenada Y, el tercero la
coordenada Z. Esto indica que el orden de los términos
de una lista es significativo y en esto se diferencia del
concepto de conjunto. Además una lista admite términos
repetidos. Dentro de las posibles listas podemos
distinguir tres casos:
Listas de un nivel
Listas anidadas
La lista vacía
TIPOS DE DATOS ESPECÍFICOS DE AUTOCAD:
CONJUNTOS DE SELECCIÓN
Los conjuntos de selección son grupos compuestos por
uno o varios objetos (entidades). Las rutinas de AutoLISP
permiten añadir o suprimir de forma interactiva objetos
de los conjuntos de selección.
NOMBRES DE ENTIDAD
Un nombre de entidad es un identificador numérico
asignado a los objetos de un dibujo. En realidad, se trata
de un puntero a un archivo mantenido por AutoCAD, en
el que AutoLISP puede encontrar el registro de la base de
datos de objetos. Este puntero suele cambiar de una
sesión de trabajo a otra.
DESCRIPTORES DE ARCHIVO
Los descriptores de archivo son identificadores
alfanuméricos asignados a archivos abiertos por Visual
LISP. Cuando sea necesario que una función de Visual
LISP lea o escriba en un archivo, debe hacerse referencia
a su identificador.
SUBRUTINAS, SUBRUTINAS EXTERNAS Y
FUNCIONES DE USUARIO
Funciones Nativas LISP o funciones externas cargadas
desde archivos compilados FAS ó VLX y archivos fuente
LISP.
TABLAS DE PAGINACIÓN
OBJETOS Y TIPOS DE DATOS ACTIVEX
Objetos propios y determinados formatos para los datos
que se pasan a los métodos ActiveX.
FUNCIÓN TYPE
Devuelve el tipo de un elemento designado
(type elemento)
LISTAS Y CONSES
Por la importancia dentro de LISP del tipo de datos LISTA,
citamos in extenso el apartado 2.4 de CLTL2,
FORMATOS DE LISTAS:
_$ ()
nil
_$ (atom nil)
T
_$ (listp nil)
T
CIERTO Y FALSO
Su negación (not nil) sería el símbolo T que representa la
condición lógica de CIERTO.
El símbolo T es también una constante en el sentido de
que sólo se representa a sí mismo.
En Visual LISP, nil y T así como otros elementos que
incluyen los operadores aritméticos (+, -. etc.) son
símbolos protegidos. Cualquier intento de asignarle otro
valor producirá un mensaje de advertencia:
(type dato)
Tipos LISP:
INT
Números Enteros
REAL
Números de Coma Decimal Flotante
LIST
Listas
STR
Cadenas
SYM
Símbolos
Tipos AutoCAD:
ENAME
Nombres de entidades
FILE
Descriptores de archivo
PAGETB
Tablas de Paginación de Funciones
PICKSET
Conjuntos de selección
SUBR
Funciones AutoLISP internas o funciones cargadas desde
archivos (FAS o VLX) compilados.
USUBR
Funciones de usuario cargadas desde ficheros fuente LSP.
EXRXSUBR
Aplicaciones ObjectARX Externas.
Tipos ActiveX:
SAFEARRAY
Matriz del tipo Safearray. Las matrices que se pasan a los
métodos ActiveX deben ser del tipo safearray. Estas
matrices son seguras (safe) porque no es posible el
asignar de manera accidental valores fuera de los límites
de la matriz provocando una excepción de datos.
VARIANT
Datos del tipo Variant. Los Variant son en esencia
estructuras que se auto-definen y que pueden contener
diferentes tipos de datos. Por ejemplo, cadenas, enteros
y matrices pueden ser representados por Variants. Junto
a los datos se guarda la información que identifica el tipo
de dato. Esta característica de auto-definición hace de los
Variant útiles medios para pasar parámetros a los
servidores ActiveX.
VLA-object
Objetos ActiveX
FUNCIONES
FUNCIONES PRIMITIVAS
ARGUMENTOS FUNCIONALES
APPLY
(apply función lista-args)
+ (suma)
(+ [número número] ...)
Si proporciona sólo un argumento número, esta función
devuelve el resultado de sumarlo a cero. Ningún
argumento, devuelve 0.
_$ (+ 1 2 3)
6
_$ (+ 1.0 2 3)
6.0
- (resta)
(- [número número] ...)
Si utiliza más de dos argumentos número, esta función
devuelve el resultado de restar del primer número la
suma de todos los números, desde el segundo hasta el
último. Si sólo utiliza un argumento número, la función
devuelve el valor resultante de restar número a cero.
Ningún argumento, devuelve 0.
_$ (- 10 1 2 3)
4
_$ (- 10 1 2.0 3)
4.0
* (multiplicación)
(* [número número] ...)
Si proporciona sólo un argumento número, esta función
devuelve el resultado de multiplicarlo por uno. Ningún
argumento, devuelve 0.
_$ (* 1 2 3)
6
_$ (* 1 2 3.0)
6.0
/ (división)
(/ [número número] ...)
Si utiliza más de dos argumentos número, esta función
divide el primer número por el producto de todos los
números del segundo al último y devuelve el cociente
final. Si proporciona sólo un argumento número, esta
función devuelve el resultado de dividirlo por uno. Ningún
argumento, devuelve 0.
_$ (/ 30 2 4)
3
_$ (/ 30 2.0 4)
3.75
1+ (incremento)
(1+ número)
Devuelve el argumento aumentado (incrementado) en 1
_$ (1+ 6)
7
1- (decremento)
(1- número)
Devuelve el argumento reducido (decrementado) en 1
_$ (1- 6)
5
EJEMPLOS:
La función CONS
Debe tenerse en cuenta que CONS, al igual que casi todas las
funciones LISP es una función no destructiva, es decir que no
altera los argumentos que recibe. Así que para conservar la
lista con el nuevo primer elemento, será necesario utilizar
SETQ. La función más general para incorporar nuevos
elementos a una lista sería entonces:
La función LIST
(list expr ...)
La función APPEND
(append lista lista ...)
(reverse lista)
(length lista)
ASSOC
SUBST
ASCII
Devuelve el código ASCII (un número entero) del primer
carácter de una cadena
_$ (ascii "Madrid")
77
CHR
Devuelve el carácter que corresponde al código ASCII (un
número entero) que se ler pasa como argumento
_$ (chr 77)
"M"
STRCAT
(strcat cadena1 [cadena2] ...)
Devuelve una cadena que es la concatenación de varias
cadenas
_$ (strcat (chr 77) "adrid")
"Madrid"
STRLEN
(strlen [cadena] ...)
Devuelve un número entero que indica la cantidad de
caracteres de una cadena
_$ (strlen "Madrid")
6
SUBSTR
(substr cadena inicio [longitud])
Devuelve una subcadena de una cadena
_$ (substr "Madrid" 3 2)
"dr"
STRCASE
(strcase cadena [cuál])
Devuelve todos los caracteres alfabéticos de una cadena
en mayúsculas o minúsculas
_$ (strcase "Madrid")
"MADRID"
_$ (strcase "Madrid" T)
"madrid"
WCMATCH
(wcmatch cadena patrón)
Realiza búsquedas con patrones de comodines en una
cadena
_$ (wcmatch "Madrid" "?a?r*")
T
_$ (wcmatch "Madrid" "?d?r*")
nil
READ
(read [cadena])
Devuelve el primer átomo o la primera lista contenida en
una cadena
_$ (read "Madrid es una ciudad")
MADRID
_$ (read (strcat "(" "Madrid es una ciudad" ")"))
(MADRID ES UNA CIUDAD)
FORMAS ESPECIALES
Así hemos creado una nueva función Lisp que puede llamarse
desde el nivel superior o anidada en otras funciones:
_$ (doble 1)
2
_$ (vl-symbol-value 'DOBLE)
#<USUBR @045677ac DOBLE>
Funciones de conversión:
Obsérvese que:
(defun infinito ( )
(setq *INF* 2.0)
(while (not (VL-INFP *INF*))
(setq *MAX-REAL* *INF* *INF* (expt *INF* 2))))
_$ (infinito)
1.#INF
_$ *MAX-REAL*
1.34078e+154
_$ *inf*
1.#INF
EXPRESIONES LAMBDA
1. El símbolo lambda
2. Una lista de parámetros
3. Una serie de expresiones que se evalúan al ejecutar la
función.
FUNCTION
Ejemplos:
El compilador Visual LISP no puede optimizar la expresión
lambda precedida de apóstrofe (QUOTE) en la siguiente
expresión:
(mapcar
'(lambda (x) (* x x))
'(1 2 3))
(mapcar
(function (lambda (x) (* x x)))
'(1 2 3))
LOAD
.vlx
.fas
.lsp
ESTRUCTURAS DE CONTROL
OPERADORES LÓGICOS
Corresponden a las operaciones Booleanas básicas. LISP
suministra tres operadores sobre valores Booleanos: and,
or, y not. De ellos, and y or son también en cierto modo
estructuras de control ya que sus argumentos son
evaluados condicionalmente. Permiten agrupar las
condiciones de prueba, casi siempre a partir de la
utilización de predicados que se utilizan dentro de las
estructuras de programación descritas más adelante.La
función not tiene un sólo argumento que analizar, y es
por ello una función simple.
ESTRUCTURAS CONDICIONALES
Visual LISP provee las dos estructuras de control
tradicionales de LISP. La estructura condicional de más
tradición en LISP es COND. Sin embargo IF resulta más
simple y es directamente comparable a las estructuras
condicionales de otros lenguajes de programación.
ATOM
Sabemos que dentro de las expresiones LISP se
distinguen los átomos y las listas. El predicado ATOM
verifica si un elemento determinado es un átomo
(atom elemento)
Devuelve nil si elemento es una lista y devuelve T en
caso contrario. Debe tenerse cuidado, si se trata de un
átomo simbólico, en tener claro si lo que se quiere
evaluar es el síbolo o su contenido. Si no está precedido
de QUOTE <'>, lo que se evalúa es el valor asociado:
_$ (atom a)
T
_$ (atom 'a)
T
_$ (setq a '(a b c))
(A B C)
_$ (atom a)
nil
_$ (atom 'a)
T
LISTP
Comprueba si un elemento es una lista
(listp elemento)
Devuelve T si elemento es una lista y devuelve nil en
caso contrario. Obsérvese que como NIL es una lista
vacía, (listp nil) devolverá T. Es decir, que todo
átomo simbólico no asociado a un valor, devoverá T
tanto para ATOM como para LISTP:
_$ !b
nil
_$ (atom b)
T
_$ (listp b)
T
_$ (listp 'b)
nil
ÁTOMOS SIMBÓLICOS:
VL-SYMBOLP
Identifica si un objeto especificado es o no un símbolo.
(vl-symbolp objeto)
Devuelve T si el objeto es un símbolo y nil si se trata de
una constante (número o cadena) o una lista.
Este predicado ha sido incorporado por Visual LISP
_$ (vl-symbolp 'a)
T
_$ (vl-symbolp 15)
nil
_$ (vl-symbolp "abc")
nil
_$ (vl-symbolp '(a b c))
nil
BOUNDP
Cuando se trata de un átomo simbólico, puede ser
necesario determinar si tiene asociado un valor. BOUNDP
verifica la existencia de dicha asociación.
(boundp sím)
Devuelve T si sím tiene un valor asociado. Si no hay
ningún valor asociado a sím (o se ha asociado a nil),
boundp devuelve nil. Si sím es un símbolo no definido, se
crea y se asocia a nil de forma automática.
NUMÉRICOS:
NUMBERP
Los átomos no simbólicos o constantes pueden ser
números o cadenas. NUMBERP comprueba si el objeto es
un número (real o entero)
(numberp elemento)
Devuelve T si elemento es un valor numérico y devuelve
nil en caso contrario. El predicado complementario
STRINGP, que comprobaría si se trata de una cadena no
está definido en Visual LISP, aunque se puede encontrar
entre los mensajes de error a la hora de depurar un
programa. En el ejemplo que sigue el mensaje "STRINGP
2" significaría que se ha recibido un valor numérico (2)
en lugar del argumento esperado del tipo cadena.
_$ (strcat 2 "b")
; error: bad argument type:
stringp 2
MINUSP
Tratándose de valores numéricos en ocasiones se deberá
comprobar si son negativos. MINUSP realiza dicha
comprobación.
(minusp número)
Devuelve T si número es negativo y nil en caso contrario.
Si el argumento no es numérico se recibirá un mensaje
de error.
ZEROP
Igual que en el caso anterior, cuando se trata de valores
numéricos ZEROP permite comprobar si un elemento se
evalúa como cero
(zerop número)
Devuelve T si número es cero y nil en caso contrario.
MEMBER
Es un tipo particular de predicado, que comprueba si un
átomo pertenece a una lista dada.
(member expr lista)
Si expr no aparece en la lista, member devuelve NIL. En
caso de encontrarlo, devuelve el resto de la lista, desde
el primer caso de la expresión encontrada. Lo devuelto
por MEMBER actúa como T (cierto) ya que cualquier valor
no nulo actuará como la negación de NIL (not NIL), es
decir, cierto. Debe tenerse cuidado en el caso de los
átomos simbólicos de pasar el nombre del símbolo
precedido de QUOTE <'>.
_$ (member a '(c d 2 4 a '(a b)
"a" 3.0 j))
nil
_$ (member 'a '(c d 2 4 a '(a b)
"a" 3.0 j))
(A (QUOTE (A B)) "a" 3.0 J)
IGUALDAD O IDENTIDAD
PREDICADOS ARITMÉTICOS
COMPARACIONES BÁSICAS:
PREDICADOS COMPUESTOS:
(<=cadnúm [cadnúm] ...)
Devuelve T si cada argumento es numéricamente menor
o igual que el situado detrás de él y nil en caso contrario
(> cadnúm [cadnúm] ...)
Devuelve T si cada argumento es numéricamente mayor
que el situado detrás de él y nil en caso contrario
(>=cadnúm [cadnúm] ...)
Devuelve T si cada argumento es numéricamente mayor
o igual que el situado detrás de él y nil en caso contrario
OPERADORES LÓGICOS
NOT
Negación lógica. Cualquier expresión cuyo valor no sea
NIL evalúa como falsa. En cambio, (not NIL) evalúa como
T. Esto sucede puesto que cualquier cosa que no tenga el
valor NIL (o la lista vacía) evalúa en LISP como T
(cierto).
OR
Devuelve el OR lógico de una lista de expresiones
(or expr...)
La función o calcula las expresiones de izquierda a
derecha en busca de una expresión distinta de nil. Si la
encuentra, OR deja de realizar cálculos y devuelve T. Si
el valor de todas las expresiones es nil, or devuelve nil.
AND
Devuelve el operador lógico AND de una lista de
expresiones
(and expr ...)
Si alguna de las expresiones da como resultado nil, se
interrumpe la operación y la función devuelve nil; en caso
contrario, devuelve T.
~ (NOT binario)
LOGAND (AND lógico binario)
LOGIOR (OR lógico binario)
BOOLE (operador lógico binario de carácter
general)
SISTEMA BINARIO
Se suelen escribir los números binarios como una secuencia de grupos de cuatro bits,
también conocidos como NIBBLES. Según el número de estas agrupaciones los números
binarios se clasifican como:
Función LSH
e incluso:
(~ <número>)
Puede que deseemos verificar un número para encontrar su
función NOT bit a bit. En este caso, se invierten todos los bits.
el valor devuelto por la función "~" será un número que tenga
todos los bits a 0 del argumento puestos a 1 y, viceversa,
todos los bits del argumento que estaban a 1 estarán a cero.
LOGAND
13 = 00001101
22 = 00010110
13 y 22 = 00000100
xxxxxx1xxxxx
o: xxxxxx0xxxxx
CONVERSIÓN DECIMAL->BINARIO
;;;Binario.lsp
;;;El ciclo continuará hasta que LSH devuelva un
valor negativo
;;;significando que se ha llegado al final de la
palabra (posición
;;;extrema izquierda). El valor binario es devuelto
en formato de lista.
;;;
;;; Función auxiliar Bit: devuelve el valor decimal
del bit en la posición indicada.
;;;
(defun Bit (pos) (lsh 1 (1- pos)))
;;;
;;;
;;Función utilizada como acumulador del valor de la
posición
;;;
(defun PosBit ()
(if (null posicion)
(setq posicion 1)
(setq posicion (1+ posicion))
) ;_ fin de if
) ;_ fin de defun
;;;
;;;
;;;Función para procesamiento del número decimal
;;;Recibe como argumento el predicado a aplicar
;;;según sea el argumento numérico positivo o
negativo
;;;
(defun ConvierteBinario (numdec predicado / posicion
numbin)
(while (not (minusp (bit (1- (PosBit)))))
(setq numbin
(cons
(if
(apply
predicado
(list (logand (bit posicion) (fix
numdec)) 0)
) ;_ fin de apply
1
0
) ;_ fin de if
numbin
) ;_ fin de cons
) ;_ fin de setq
) ;_ fin de while
) ;_ fin de defun
;;;
;;;Función principal
;;;Tiene en cuenta si se trata de un número positivo
o negativo:
;;;
(defun Binario (numdec /)
(if (minusp numdec)
(ConvierteBinario (~ numdec) '=)
(ConvierteBinario numdec '/=)
) ;_ fin de if
) ;_ fin de defun
CONVERSIÓN BINARIO->DECIMAL
;;;DECIMAL.LSP
;;;Recibe un número binario y lo convierte en decimal
;;;debe comprobar el tipo de dato recibido,
;;;que puede ser cadena, número o lista
;;;
;;;Función utilitaria BIT
;;;devuelve el valor decimal del bit en la posición
recibida:
;;;
(defun Bit (pos) (lsh 1 (1- pos)))
;;;
;;;
;;;Función utilizada como acumulador del valor de la
posición:
;;;
(defun PosBit ()
(if (null posicion)
(setq posicion 1)
(setq posicion (1+ posicion))
) ;_ fin de if
) ;_ fin de defun
;;;
;;;PREDICADOS DEFINIDOS PARA ESTA FUNCIÓN:
;;;Como filtro de entrada se emplean tres predicados
;;;definidos expresamente para ella:
;;;STRINGP, STD-DOTTED-PAIR-P y BINARIOP
;;;
;;;Predicado STRINGP
;;;Comprueba si el dato es una cadena
;;;(ver PREDICADOS DEFINIDOS POR EL USUARIO)
;;;
(defun stringp (dato) (equal (type dato) 'STR))
;;;
;;;Predicado STD-DOTTED-PAIR-P
;;;Comprueba de que se trate de un par punteado:
;;;Adaptado de la Standard Lisp Library
;;;de Reini Urban:
;;;
(defun STD-DOTTED-PAIR-P (lst)
(and (vl-consp lst) (not (listp (cdr lst))))
) ;_ fin de defun
;;;
;;;Predicado Binariop
;;;Comprueba que la lista incluya valores sólo 0 ó 1
;;;
(defun Binariop (numbin /)
(apply 'and
(mapcar '(lambda (term) (or (equal term 0)
(equal term 1)))
numbin
) ;_ fin de mapcar
) ;_ fin de apply
) ;_ fin de defun
;;;
;;;Función utilitaria NUMLISTA
;;;Recibe cualquier número decimal y devuelve los
dígitos aislados
;;;en formato de lista
;;;Si el número es real, lo trunca despreciando los
decimales
;;;
(defun Numero->Lista (numero / lista)
(while (> numero 0)
(setq lista (cons (rem (fix numero) 10) lista))
(setq numero (/ (fix numero) 10))
) ;_ fin de while
lista
) ;_ fin de defun
;;;
;;;Función utilitaria Cadena->Lista
;;;Recibe una cadena de caracteres y devuelve los
caracteres
;;;aislados en formato de lista
;;;
(defun Cadena->Lista (cadena)
(mapcar
'chr ;3.- convierte los
códigos ASCII a caracteres
(vl-string->list ;2.- convierte la
cadena a lista de códigos ASCII
cadena
) ;_ fin de vl-string->list
) ;_ fin de mapcar
) ;_ fin de defun
;;;
;;;Función ConvierteDecimal
;;;Realiza la conversión, al ser la evaluación
siempre de izquierda a derecha,
;;;debe invertir la lista para iniciar la
comprobación por el bit último de la derecha.
;;;Esta comprobación se hace mediante el mapeado de
una función LAMBDA a la lista,
;;;que comprueba si el número es cero y en caso que
no lo sea, inserta el valor
;;;decimal del bit en la lista
;;;Una vez concluido este mapeado, se le aplica la
funcióm '+ para sumar todos
;;;los valores, con lo que obtenemos el resultado de
la conversión Binario->Decimal.
;;;Devuelve un número decimal
;;;
(defun ConvierteDecimal (numbin / posicion)
(if (Binariop numbin)
(apply
'+ ; suma los valores de
la lista devuelta por MAPCAR
(mapcar
(function
(lambda (x)
(PosBit) ;5.- valora la
variable posicion
(if (not (zerop x))
(bit posicion)
0 ; en caso
contrario devuelve cero
) ;_ fin de if
) ;_ fin de lambda 7.- y los
valores devueltos quedan en una lista
) ;_ fin de function
(reverse numbin)
) ;_ fin de mapcar
) ;_ fin de apply
nil
) ;_ fin de if
) ;_ fin de defun
;;;
;;;Función filtro de entrada, considerando los
posibles tipos de entrada:
;;;Lista, cadena o número
;;;los únicos términos aceptados serán en cualquier
caso ceros o unos
;;;de detectarse otro valor, la función devuelve NIL
;;;Otro error derivaría de que la lista fuera un par
punteado
;;;Para comprobarlo utilizaríamos la función STD-
DOTTED-PAIR-P adaptada de
;;;la Standard LISP library de Reini Urban
;;;
(defun Decimal (numbin /)
(cond
((STD-DOTTED-PAIR-P numbin)
(terpri)
(princ numbin)
(princ " no es un valor admitido.")
(princ)
)
((listp numbin)
;;si es lista, convierte los términos
(setq
numbin ;;que sean cadenas en números (o átomos
simbólicos si fueran letras)
(mapcar
(function (lambda (term)
(if (stringp term)
(read term)
term
) ;_ fin de if
) ;_ fin de lambda
) ;_ fin de function
numbin
) ;_ fin de mapcar
) ;_ fin de setq
(ConvierteDecimal numbin)
)
((numberp numbin)
;;si es número lo convierte en cadena para
después hacerlo lista
(setq numbin (Numero->Lista numbin))
(ConvierteDecimal numbin)
)
((stringp numbin)
(setq numbin (mapcar 'read (Cadena->Lista
numbin)))
(ConvierteDecimal numbin)
)
(t
(terpri)
(princ numbin)
(princ " no es un valor admitido.")
(princ)
)
) ;_ fin de cond
) ;_ fin de defun
LOGIOR
ALGUNOS EJEMPLOS:
(= 1 (getvar "cmdactive")),
se debe utilizar:
Desactivar REFENT:
BOOLE
(boole 6 6 5) devuelve 3
INTEGERP
Comprueba si el argumento es un número entero.
REALP
Comprueba si el argumento es un número real.
STRINGP
Comprueba si el argumento es una cadena.
ENAMEP
Comprueba si el argumento es un nombre de entidad.
FILEP
Comprueba si el argumento es un identificador de archivo.
PAGETBP
Comprueba si el argumento es una Tablas de Paginación de
Funciones.
SUBRP
Comprueba si el argumento es una Función AutoLISP interna o
compilada.
USUBRP
Comprueba si el argumento es una función de usuario cargada
desde un fichero fuente LSP.
EXRXSUBRP
Comprueba si el argumento es una Aplicación ObjectARX
Externa.
SAFEARRAYP
Comprueba si el argumento es una Matriz del tipo Safearray.
VARIANTP
Comprueba si el argumento es del tipo Variant.
VLA-OBJECT-P
Comprueba si el argumento es un Objeto ActiveX.
ESTRUCTURAS CONDICIONALES
CONDICIONAL IF
ESTRUCTURA IF-THEN-ELSE
Evalúa expresiones condicionalmente:
(if expr_prueba expr_then [expr_else])
Si expr_prueba no es nil, evalúa expr_then;
en caso contrario evalúa expr_else.
La función if devuelve el valor de la expresión
seleccionada. Si expr_else no existe y expr_prueba es
nil, entonces la función if devuelve nil.
Función PROGN
Calcula las expresiones secuencialmente y devuelve el
valor de la última expresión
(progn [expr]...)
Se suele utilizar progn para calcular varias expresiones
cuando sólo se espera, como en cada una de las ramas
del IF, una sola expresión.
COND
Se utiliza como la función condicional primaria de Visual
LISP
(cond (prueba1 resultado1 ...) ...)
FUNCIONES RECURSIVAS
(trace busca_en_lista)
Los datos que sirven para definir cada una de los objetos
gráficos de AutoCAD están organizados en forma de una lista
de asociación, es decir, una lista de listas, donde la
información guardada en cada sublista se identifica mediante
un código numérico (entero) que aparece como el primer
término (CAR) cada sublista. El significado de cada código
coincide en términos generales con los códigos que identifican
los datos contenidos en los archivos del formato de fichero DXF
utilizado para la transferencia de dibujos AutoCAD a otras
aplicaciones. Para el desarrollo de la siguiente función basta
saber que los valores que corresponden a las coordenadas X e
Y de cada vértice aparecen en sucesivas sublistas identificadas
mediante el código de asociación 10. La coordenada Z aparece
en una única sublista (ya que debe ser la misma para todos los
vértices) identificada mediante el código 38. La función
recursiva descrita a continuación será llamada desde otra
función que permita seleccionar un objeto del dibujo,
compruebe a continuación que se trata de la entidad deseada
(del tipo "LWPOLYLINE"), extraiga del objeto gráfico
seleccionado la correspondiente lista de asociación y la pase,
junto con el valor de la elevación, como argumentos a la
función recursiva de extracción VertPoly.
Función ExtraeVertices:
FUNCIONES ITERATIVAS
ITERACIONES SIMPLES
REPEAT
Evalúa cada expresión el número de veces que se
especifique en el argumento entero, y devuelve el valor
de la última expresión
(repeat entero expresión ...)
El argumento entero debe ser un número entero
positivo.
FOREACH
Evalúa cada expresión para todos los miembros de una
lista
(foreach nombre lista expresión ...)
Recorre la lista, asignando sucesivamente cada elemento
de la lista a la variable nombre y evaluando de esta
manera cada expresión para cada uno de los elementos
de la lista representados por nombre. Puede especificar
tantas expresiones como desee. La función FOREACH
devuelve el resultado de la última expresión evaluada.
VLAX-FOR
Efectúa una iteración a través de una colección de
objetos evaluando cada expresión
(vlax-for símbolo colección [expresión1
[expresión2 ...]])
El argumento símbolo será asignado a cada Objeto-VLA
de la colección. El argumento colección representa un
objeto ActiveX del tipo colección. [expresión1
[expresión2 ...]] son las expresiones a evaluar. La
función devuelve el valor de la última expresión evaluada
para el último objeto de la colección.
;;;Listado de entidades
;;;Salida por pantalla
(defun disp_l (lis_e /)
(foreach sublista lis_e
(print sublista)
) ;_ fin de foreach
(princ) ;evita la repetición de la
última línea
) ;_ fin de defun
Command: (entget(car(entsel)))
Select object: ((-1 . <Entity name: 1487558>) (0 .
"LWPOLYLINE") (330 . <Entity
name: 14874f8>) (5 . "2B") (100 . "AcDbEntity") (67 .
0) (410 . "Model") (8 .
"0") (100 . "AcDbPolyline") (90 . 4) (70 . 0) (43 .
0.0) (38 . 0.0) (39 . 0.0)
(10 99.3432 134.975) (40 . 0.0) (41 . 0.0) (42 . 0.0)
(10 130.202 185.828) (40
. 0.0) (41 . 0.0) (42 . 0.0) (10 202.747 163.648) (40
. 0.0) (41 . 0.0) (42 .
0.0) (10 269.336 215.041) (40 . 0.0) (41 . 0.0) (42 .
0.0) (210 0.0 0.0 1.0))
;La lista impresa en pantalla resulta difícil de
leer, al no estar ordenada en columna
Command: (disp_l (entget(car(entsel))))
(-1 . <Entity name: 1487558>)
(0 . "LWPOLYLINE")
(330 . <Entity name: 14874f8>)
(5 . "2B")
(100 . "AcDbEntity")
(67 . 0)
(410 . "Model")
(8 . "0")
(100 . "AcDbPolyline")
(90 . 4)
(70 . 0)
(43 . 0.0)
(38 . 0.0)
(39 . 0.0)
(10 99.3432 134.975)
(40 . 0.0)
(41 . 0.0)
(42 . 0.0)
(10 130.202 185.828)
(40 . 0.0)
(41 . 0.0)
(42 . 0.0)
(10 202.747 163.648)
(40 . 0.0)
(41 . 0.0)
(42 . 0.0)
(10 269.336 215.041)
(40 . 0.0)
(41 . 0.0)
(42 . 0.0)
(210 0.0 0.0 1.0)
;;;Función ListaCapas
;;;Devuelve una lista con los nombres de capas
ordenados alfabéticamente
;;;Obsérvese la posibilidad aquí demostrada de tener
una función y una variable
;;;con idéntico nombre en un mismo entorno. Para ello
será necesario que dicha
;;;variable, en este caso ListaCapas sea una variable
local de la función.
(setq *EsteDibujo* (vla-get-ActiveDocument (vlax-get-
acad-object)))
;se ejecuta previamente
(defun ListaCapas (/ ListaCapas)
(vlax-for objeto-VLA (vlax-get *EsteDibujo*
"Layers")
(setq ListaCapas (cons (vlax-get objeto-VLA
"Name") ListaCapas))
;el mismo nombre se utiliza para la variable
local que guarda
;temporalmente el listado de capas.
) ;_ fin de vlax-for
(acad_strlsort ListaCapas)
) ;_ fin de defun
Ejemplo de su utilización:
;;;SEL2LST
;;;Recibe un conjunto de selección y devuelve una
lista
(defun sel2lst( sel / n l)
(repeat (setq n (sslength sel))
(setq n (1- n) l (cons (ssname ss n) l))
)
)
;;;LST2SEL
;;;La función opuesta, recibe una lista y devuelve un
conjunto de selección
(defun cdnr ( n l )
(repeat n
(setq l (cdr l))
)
)
Como se ve, esta puede ser otra vía para abordar el desarrollo
del predicado PALINDROMOP: obtener mediante STRTOL la
cadena de caracteres en forma de lista y entonces compararla
con la lista invertida por reverse:
WHILE
Evalúa una expresión de prueba y, si ésta no da como
resultado nil, evalúa otras expresiones para volver de
nuevo a la expresión de prueba
(while expr_prueba expr...)
La función while continúa hasta que expr_prueba es nil
. Entonces devuelve el valor más reciente de la última
expresión.
Ejemplos de Iteraciones con WHILE:
CONTEO DE ENTIDADES:
FUNCIONES DE MAPEADO
MAPCAR
(mapcar función lista1 ... listan)
MAPCAR opera sobre los elementos sucesivos de las listas.
Primero se aplica la función al CAR de cada lista, entonces
al CADR de cada una y así sucesivamente. Lo ideal sería
que todas las listas fueran de igual longitud. Si no lo
fueran, la iteración concluye al agotarse la lista más corta
y los elementos en exceso de las demás listas se ignoran.
MAPCAR devuelve una lista con los resultados de las
sucesivas llamadas a la función. Por ejemplo:
_1$ (mapcar 'cons '(a b c) '(1 2 3))
((A . 1) (B . 2) (C . 3))
Una expresión-LAMBDA puede ser utilizada con MAPCAR.
Esto resulta útil cuando algunos de los argumentos de
función son constantes o se proporcionan mediante
variables u otros métodos.
Y hay algo más. Digamos que tengo esta función que SUMA
todos sus argumentos. Es un '+, que puedo invocar como:
(+ 1 2) ó (+ 1 2 3) etc.
Fuente:
Subject: A short course in LISP, LAMBDA,
QUOTE, MAPCAR...
Date: Sat, 02 Nov 1996 08:50:38 -0800
From: Lu <learly@ix.netcom.com>
VLAX-MAP-COLLECTION
Aplica una función a todos los objetos de una colección.
(vlax-map-collection colección función)
El argumento colección representa un Objeto-VLA de tipo
colección. El argumento función será un símbolo o una
expresión-LAMBDA que será aplicada a colección.
Ejemplo:
;;;QUITAR-SI
;;;Implementación de la función REMOVE-IF
;;;en contexto AutoLISP
(defun quitar-si (predicado lista)
(cond
((null lista) nil)
((apply predicado (list (car lista)))
(quitar-si predicado (cdr lista))
)
(t (cons (car lista)(quitar-si predicado (cdr
lista))))
)
)
;;;QUITAR-SI-NO
;;;Implementación de la función REMOVE-IF-NOT
;;;en contexto AutoLISP
(defun quitar-si-no (predicado lista)
(cond
((null lista) nil)
((apply predicado (list (car lista)))
(cons (car lista)(quitar-si-no predicado (cdr
lista)))
)
(t (quitar-si-no predicado (cdr lista)))
)
)
Command: (entget(car(entsel)))
Select object:
((-1 . <Entity name: 43b0500>) (0 . "LWPOLYLINE")
(5 . "20")
(100 . "AcDbEntity") (67 . 0) (8 . "0") (100 .
"AcDbPolyline")
(90 . 7) (70 . 0) (43 . 0.0) (38 . 0.0) (39 . 0.0)
(10 103.882 154.494) (40 . 0.0) (41 . 0.0) (42 . 0.0)
(10 180.771 201.906) (40 . 0.0) (41 . 0.0) (42 . 0.0)
(10 186.224 143.05) (40 . 0.0) (41 . 0.0) (42 . 0.0)
(10 267.476 167.028) (40 . 0.0) (41 . 0.0) (42 . 0.0)
(10 256.569 105.994) (40 . 0.0) (41 . 0.0) (42 . 0.0)
(10 298.558 123.432) (40 . 0.0) (41 . 0.0) (42 . 0.0)
(10 293.651 89.1) (40 . 0.0) (41 . 0.0) (42 . 0.0)
(210 0.0 0.0 1.0))
_$ (entget(car(entsel)))
((-1 . <Entity name: 43b0500>) (0 . "LWPOLYLINE")
(5 . "20")
(100 . "AcDbEntity") (67 . 0) (8 . "0") (100 .
"AcDbPolyline")
(90 . 7) (70 . 0) (43 . 0.0) (38 . 0.0) (39 . 0.0)
(10 103.882 154.494 0.0) (40 . 0.0) (41 . 0.0) (42 .
0.0)
(10 180.771 201.906 0.0) (40 . 0.0) (41 . 0.0) (42 .
0.0)
(10 186.224 143.05 0.0) (40 . 0.0) (41 . 0.0) (42 .
0.0)
(10 267.476 167.028 0.0) (40 . 0.0) (41 . 0.0) (42 .
0.0)
(10 256.569 105.994 0.0) (40 . 0.0) (41 . 0.0) (42 .
0.0)
(10 298.558 123.432 0.0) (40 . 0.0) (41 . 0.0) (42 .
0.0)
(10 293.651 89.1 0.0) (40 . 0.0) (41 . 0.0) (42 .
0.0)
(210 0.0 0.0 1.0))
_$
Hay muchos problemas para los cuales la recursión resulta natural y para los cuales
la iteración resulta extremadamente difícil. Esto sucede usualmente cuando se tienen
en cuenta objetos con una estructura compleja de listas anidadas. Por ejemplo,
consideremos esta expresión matemática en formato LISP:
(setq math-formula
'(+ 3 (* (- 5 pi) 4 (/ 3 7))(* 15 2))
)
>(num-nums math-formula)
1> (NUM-NUMS (+ 3 (* (- 5 PI) 4 (/ 3 7)) (* 15 2)))
2> (NUM-NUMS (3 (* (- 5 PI) 4 (/ 3 7)) (* 15 2)))
3> (NUM-NUMS ((* (- 5 PI) 4 (/ 3 7)) (* 15 2)))
4> (NUM-NUMS (* (- 5 PI) 4 (/ 3 7)))
5> (NUM-NUMS ((- 5 PI) 4 (/ 3 7)))
6> (NUM-NUMS (- 5 PI))
7> (NUM-NUMS (5 PI))
8> (NUM-NUMS (PI))
9> (NUM-NUMS NIL)
<9 (NUM-NUMS 0)
<8 (NUM-NUMS 0)
<7 (NUM-NUMS 1)
<6 (NUM-NUMS 1)
6> (NUM-NUMS (4 (/ 3 7)))
7> (NUM-NUMS ((/ 3 7)))
8> (NUM-NUMS (/ 3 7))
9> (NUM-NUMS (3 7))
10> (NUM-NUMS (7))
11> (NUM-NUMS NIL)
<11 (NUM-NUMS 0)
<10 (NUM-NUMS 1)
<9 (NUM-NUMS 2)
<8 (NUM-NUMS 2)
8> (NUM-NUMS NIL)
<8 (NUM-NUMS 0)
<7 (NUM-NUMS 2)
<6 (NUM-NUMS 3)
<5 (NUM-NUMS 4)
<4 (NUM-NUMS 4)
4> (NUM-NUMS ((* 15 2)))
5> (NUM-NUMS (* 15 2))
6> (NUM-NUMS (15 2))
7> (NUM-NUMS (2))
8> (NUM-NUMS NIL)
<8 (NUM-NUMS 0)
<7 (NUM-NUMS 1)
<6 (NUM-NUMS 2)
<5 (NUM-NUMS 2)
5> (NUM-NUMS NIL)
<5 (NUM-NUMS 0)
<4 (NUM-NUMS 2)
<3 (NUM-NUMS 6)
<2 (NUM-NUMS 7)
<1 (NUM-NUMS 7)
7
(1)
La funciones iterativas son usualmente más rápidas que
sus contrapartes recursivas. Si la velocidad es
importante, normalmente usaríamos la iteración.
(2)
Si la memoria de pila es un limitante, se preferirá la
iteración sobre la recursión.
(3)
Algunos procedimientos se programan de manera
recursiva de forma muy natural, y resultan prácticamente
inabordables iterativamente. Aquí la elección es clara.
;;;----------------------INICIO DEL
PROGRAMA---------------------------------------------
----
;;;(c) Reinaldo Togores, Santander, 1997.
;;Listado de entidades
;;Salida por pantalla
;;Rutina DISP_L: Escribe lista de entidad en pantalla
(defun disp_l ( lis_e / )
(repeat (length lis_e)
(princ (nth cont lis_e))(terpri)
(setq cont (1+ cont))
)
)
;;Rutina PRN_L: Escribe lista de entidad en fichero
de texto
(defun prn_l (lis_e / )
(repeat (length lis_e)
(print (nth cont lis_e) if_w)
(setq cont (1+ cont))
)
)
;;Rutina OPN_L: Abre fichero de texto para escribir
lista de entidad
(defun opn_l ()
(if (null nomls)(setq nomls ""))
(if (setq nomls (getfiled "Lista de Entidad:"
nomls "txt" 5))
(if (wcmatch nomls "*`.*")
(setq if_w (open nomls "w"))
(setq if_w (open (strcat nomls ".txt") "w")
)
)
)
)
;;Programa Principal LISTX: Escribe la lista de
entidad a un fichero de texto
(defun c:listx ( / n_e lis_e ctrl cont)
(if (setq s_set (ssget))
(progn
(opn_l)(setq s_c 0)
(repeat (sslength s_set)
(if (setq n_e (ssname s_set s_c))(setq
lis_e (entget n_e (list "*"))))
(setq cont 0)
(prn_l lis_e)
(if
(or
(= (cdr (assoc 0 lis_e))
"POLYLINE")
(= (cdr (assoc 0 lis_e)) "INSERT")
)
(progn
(setq ctrl t)
(while (and ctrl (setq n_e (entnext
n_e)))
(setq cont 0)
(if (= (cdr (assoc 0 (setq lis_e
(entget n_e (list "*"))))) "SEQEND")
(progn (setq ctrl nil)(prn_l
lis_e))
(prn_l lis_e)
)
)
)
)
(setq s_c (1+ s_c))
)
(close if_w)
)
)
)
;;Programa Principal LIS: Salida por pantalla
(defun c:lis ( / n_e lis_e ctrl cont)
(if (setq n_e (car (entsel)))(setq lis_e (entget
n_e (list "*"))))
(setq cont 0)
(disp_l lis_e)
(setq ctrl t)
(while (and ctrl (setq n_e (entnext n_e)))
(setq cont 0)
(if (= (cdr (assoc 0 (setq lis_e (entget n_e
(list "*"))))) "SEQEND")
(setq ctrl nil)
(disp_l lis_e)
)
)
)
(alert "Lista de Entidades\teclee LIS para salida por
pantalla\nTeclee LISTX para salida a fichero...")
;;;----------------------FIN DEL
PROGRAMA---------------------------------------------
----
S O L U C I O N E S C O N
SPLINE
por Reinaldo Togores
PLINE.DWG
LA SOLUCIÓN SpLINE
De SpLINES a PolyLINES:
C O N V E R S I Ó N
PolyLINE>SpLINE
Many of you have posted requests to the Wizard's Lab asking how to use Visual LISP® to
interface with other automated productivity tools like Microsoft® Excel. This article is the first in a
series that shows you how to create such an interface. First I'll explain the basics and then we'll
start looking at the larger task: using Visual LISP to tie other automated programs into your CAD
drawings. As you'll see it's all about productivity gains.
Note: You can download the code used in this tutorial from here (1.5 KB).
What Is Automation?
Microsoft Windows holds some very powerful automation programs that, with some programming,
you can use to control (drive) other programs you've installed. Generally this mechanism is
referred to as automation. The Windows operating system has a series of guidelines that you
can follow to create an application that accepts automation.
An automated program publishes or sends information to the operating system, which defines the
types of commands the program supports and how other programs are to go about
communicating with it. That process is basically standardized. But as you might have guessed,
beyond that each program has its own unique way for other programs to drive it automatically.
Automating certain tasks can achieve marvelous results. A simple illustration is to tie drawing
information from a bill of materials into a spreadsheet for further analysis or reporting purposes.
Another: to import text generated by a word-processing application into a drawing so that you
don't have to reenter data. Or this: integrating a database containing details about elements used
in the drawing to determine loads or costs. Your only limit here is the power of your imagination.
This arena is a wonderful place for wizards to play!
Beginning a Conversation
First, let's look at the theory behind automation and then look at how to apply that theory in Visual
LISP.
The automation process begins when one program establishes a communications link to another
program. Any application built to run on the Windows OS can be automated if the developer
provides the required parts. During installation, the name of each application is placed in the
Windows Systems Registry and that name is used to reference the application from another
program. Learning that System Registry name is your first step. In most cases you can guess at
the name with reasonable accuracy. The name to activate a program such as Microsoft Excel is
simply Excel.Appliction. The dot that appears between the name Excel and the word Application
is a standard divider used when referencing an object inside Windows. If the application name is
not obvious then you have some detective work to do. Most applications will include some
documentation about automation that will provide assistance.
Once you know the name of the application object, open the application. (Of course it should go
without saying that you can't automate an application that isn't already installed on your PC.) To
begin automating an application, you can either communicate with an instance of the program
that is already running or you can create a new instance of the program. That is, you can open an
existing channel to the program or create a new channel by copying the program to memory and
working with the copy.
The requirements of your interface dictate your choice here. If you want to create a new
document or worksheet, create a new channel to the application. If you want to look up
information in the program, open the program. The main difference is in how much time is
required to establish communication. Opening an existing application in memory is significantly
faster than creating a new instance.
The following list defines a few terms that you should know as you begin to create an interface
that automates an application. One word of caution though, all the documents about automation
are likely written from the perspective of Visual BASIC and not Visual LISP. As a Visual LISP
programmer, you must translate this information into more relevant terms. We'll get to that a bit
later on. Here's the list of terms:
(VL-LOAD-COM) returns instantly if the VLAX functions are already available in memory, and
there is no degradation to your programs if you do end up calling more than once. I just include
(VL-LOAD-COM) in the base load of the application by placing it in my source file outside of a
function definition. That way, it is evaluated as the function set is loaded into AutoCAD.
This does slow down the loading process by a few seconds, but not to an unacceptable degree.
And I let the user know that a process is loading by issuing a prompt to the command line. VL-
LOAD-COM does not present a prompt. Without the prompt that I supply, the user may think the
process has frozen the computer or will complain about how slow my program is.
Once VLAX functions are available you can use one of three different functions to obtain an
object reference to the application with which you wish to communicate. Your application
requirements dictate which function you choose. The fastest options are to link to an already
running instance of the program with which you want to interface.
You must supply the name of the application you will be automating for each of these methods. If
the application is not available, a nil result is returned indicating that your request for an
automation link has failed. Otherwise, an object reference is returned. Save that result using
SETQ in your Visual LISP program. The following expression attempts to link to either a running
copy of Excel or to create a new instance of it if one is not currently available.
(setq xL (vlax-get-or-create-object
"Excel.Application"))
When this expression has completed, the symbol xL will be nil if Excel could not be located on the
computer. Otherwise xL contains the object reference to a running copy of the program. You will
use the object reference from this point forward to communicate with that specific instance of
Excel.
If you want to be sure that you are working with an exclusive instance of Excel, use the (VLAX-
CREATE-OBJECT) method. That way, if Excel is already running, a new copy is started in the
computer so that your application does not interfere with any other spreadsheets that may be
open and in use. Or use (VLAX-GET-OBJECT) and simply use an already running copy of Excel.
This option is useful when you are working with an active spreadsheet and simply want to move
data between it and a drawing. In all the cases, you will get an object reference if the link is
successful or a nil result if the link could not be established.
What is an object library? It is a set of subroutines and data elements that the hosting application
has declared as being available to external tasks employing automation techniques. The
Windows OS uses a standard mechanism known as Object Linking and Embedding (OLE for
short) that programming systems can tie in to. OLE is an advanced method of program-to-
program communications allowing automation to take place.
Object libraries are declared in a file with an OLB extension, which you can usually find in the
same folder as the application executable files. Use Windows Explorer to locate the file (use the
Find option in the Tools drop-down menu and search for all files that have an extension of OLB).
You can either code the entire path to the object library into your application, use a variable to
house that information in case you want to change it later on, or have your program locate the file
you need.
Use the Visual LISP function (VLAX-IMPORT-TYPE-LIBRARY) to import the library into your
program. On import, a new host of functions becomes available to use in the interface you're
creating. Do this import only once in an application.
The Visual LISP documentation recommends that the import function be run at the topmost level
in your program, just like the (VL-LOAD-COM) expression. That method prevents attempts at
loading the type library more than once. You don't want to import a library more than once
because it is time consuming and could possibly foul symbol assignments you have already
made. Another way to prevent importing a library twice is to employ a conditional as in the
following expression:
Let's take a closer look at the VLAX-IMPORT-TYPE-LIBRARY expression in this code segment.
The first step is to determine if the function XL-OPEN is already defined. The name XL-OPEN
does not exist in normal Visual LISP and should have a null binding when the expression is first
encountered. You are going to create VL-OPEN as a result of the library import. You call this
function by supplying a string in the VLAX-IMPORT-TYPE-LIBRARY to serve as a common prefix
for all the new functions to be imported.
There are eight parameters containing four values supplied to the VLAX-IMPORT-TYPE-
LIBRARY expression. Each value is supplied as a pair. The first part is a symbol name that starts
with a colon character " : " as in :tlb-filename. These symbols are constants inside Visual LISP
and are used by the VLAX-IMPORT-TYPE-LIBRARY function to identify what the immediately
following string represents. Following the constant :TLB-FILENAME is the file name, with path, of
the type library. In the above example the file is located in the Microsoft Office program directory.
The remaining three pairs are for prefixes to be added to the names of the methods, properties,
and constants of the imported library. The prefixes should be unique so that they do not interfere
with other functions already defined inside Visual LISP.
In the example the prefixes used are "XL-", "XLP-" and "XLC-". Thus VL-OPEN will have a
binding once the import has taken place, and the IF test will not allow the import to take place a
second time. This is why I strongly recommend using a conditional expression in association with
the import of an object library. If the import has taken place previously, then VL-OPEN will not be
nil and the NULL test will return a false answer, which prevents subsequent calls to the import.
You must interpret this information from a Visual BASIC programming point of view because most
automation programming is done using Visual BASIC not Visual LISP. However, using the
methods described so far—especially the import object library function—Visual LISP is just as
powerful a tool. But remember that it is only found in AutoCAD.
Remember that the list only reveals object names. You must go into the other application and find
the associated information in the help system.
What's Next?
So far you've learned that you can use Visual LISP to interface with another program such as
Excel through the use of the OLE or automation system. You've also seen how to establish a link
to the automation tools from another application.
The next steps involve the actual sending and receiving of data using the interface. This requires
that you use either the VLAX-GET and VLAX-PUT functions or that you allow Visual LISP to
import a new library of functions as defined in an OLB (Object Library) file. Check out the scrolls
and potions in the Wizard's Lab to see how this might be accomplished or join me next month
when we'll explore how to transfer data to Excel.
Continuing on, we want to create a command called ZAP that erases all the objects
in the drawing. To do this we'll need to execute the ERASE command, input the ALL
option (all three characters), and then an additional ENTER to get out of the
command.
So how do we tell Visual LISP to hit ENTER? A pair of double quotes will do. For those
of you who have customized menus, this is the equivalent of using a semicolon to
represent an ENTER.
(defun c:ZAP ()
(command "erase" "all" "")
)
So how are we going to create a square? We'll execute the POLYGON command,
make the object four-sided, select the EDGE option, and pause twice to let the user
pick the two corners that will make up our square. If you're lost, please go into the
POLYGON command in AutoCAD and go through all of these steps manually. Let's
look at the code.
(defun c:SQ ()
(command "polygon" "4" "E" pause pause)
)
Using pause in a Visual LISP routine will instruct the program to wait for user input.
Those two pauses will enable the user to pick the two corners of the square. This is
equivalent of the backslash \ in menu customization. So what do we have so far?
(defun c:ZP ()
(command "zoom" "p")
)
(defun c:ZAP ()
(command "erase" "all")
)
(defun c:SQ ()
(command "polygon" "4" "e" pause pause)
)
Figure 1. The APPLOAD dialog makes it easy to load your Visual LISP
routines.
Test-Drive Your New Commands
Now we're ready to load and run our routines. We'll use the APPLOAD command to
load our newly created Visual LISP routine. This can be found in the Tools pull-down
menu under Load Application as a dialog box, as shown in Figure 1.
You'll simply select the file you just created and load it. If you've made any errors,
this dialog will be happy to notify you. If all goes as planned, you should now be able
to key in ZP, ZAP, and SQ at the command line. Give them a try.
Now that was pretty easy, wasn't it? This is just a peek into the powerful world of
Visual LISP. For more, you should look to the guru, Bill Kramer, to lead you down the
path of true programming enlightenment.
Until next month...Happy AutoCADing! I wish you all a wonderful holiday season.