2.5.3.1. Extracción de Vértices: Una Solución Más Eficaz


El programa expuesto en el capítulo sobre los procedimientosrecusivos, ha sido elaborado utilizando sólo las funciones primitivasconocidas entonces de AutoLISP. Una manera más eficaz de encarar suanálisis sería la de tener en cuenta si algunos de los procesosque se llevan a cabo dentro del mismo pudieran programarse independientemente,como funciones utilitarias. Una función utilitaria esun nuevo operador que añadimos al lenguaje de programación pararesolver situaciones que pueden presentarse con cierta frecuencia dentro denuestros programas. En el caso que estamos analizando, podemos concebir lanecesidad de una nueva función que recorra una lista y de acuerdo con elresultado de una función que se le pase como predicado elimine unostérminos y conserve otros. Como resultado tendríamos una listaque sólo incluyera los términos deseados, en este caso las listasde asociación identificadas con los códigos 10. Estas funcionesque pudiéramos llamar QUITAR-SI y su complementaria QUITAR-SI-NO han estado siempre entre las primeras que losprogramadores LISP han añadido a su repertorio de utilidades. Unadefinición de las mismas pudiera ser:

;;;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))) ))

Tanto es así que se han incorporado como operadores a la norma deCommon LISP bajo los nombres de REMOVE-IF (QUITAR-SI) y de REMOVE-IF-NOT (QUITAR-SI-NO). Visual LISP les incorpora el prefijo VL-para distinguirlos de las funciones del antiguo AutoLISP, y así lasencontraremos en el catálogo de funciones Visual LISP como VL-REMOVE-IFy VL-REMOVE-IF-NOT.

Utilizando VL-REMOVE-IF-NOT y con la ayuda de la función demapeado sobre listas MAPCAR, estudiada en el apartado anterior, nuestrafunción VertPoly pudiera escribirse de la siguiente manera:

;;;Función VertPoly utilizando VL-REMOVE-IF-NOT;;;Paso 1:;;;Eliminar todos las sublistas que no correspondan al código 10;;;Paso 2:;;;Extraer el CDR de cada una de las sublistas.;;;Se utiliza el mapeado de la función CDR a la lista mediante MAPCAR;;;Paso 3:;;;Se añade el valor de la elevación mapeando a la lista una expresión LAMBDA.(defun VertPoly (lista elevacion) (mapcar ;Paso 3 (function (lambda (x) (append x (list elevacion)))) (mapcar ;Paso 2 'cdr (vl-remove-if-not ;Paso 1 (function (lambda (x) (equal (car x) 10))) lista ) ;_ fin de vl-remove-if-not ) ;_ fin de mapcar ) ;_ fin de mapcar) ;_ fin de defun

Obsérvese el uso de la función FUNCTION que fuerza lacompilación de las dos expresiones-LAMBDA. Se utiliza en lugar de QUOTE (o apóstrofe) procurando una mayor eficacia y rapidez.

Esta segunda formulación resulta más clara y máseconómica en cuanto a esfuerzo de programación. Por otra parte,supera las limitaciones de Visual LISP en cuanto a la recursión quepudiera provocar en caso de polilíneas con un gran número devértices un error por desbordamiento de pila.

Nota: esta función devuelve los resultados correctos para Visual LISPen AutoCAD 2000. Sin embargo hemos encontrado que falla si se ejecuta desde elIDE Visual LISP para la versión 14. El error se encuentra en los valoresde la lista de asociación para el objeto LWPOLYLINE que se obtienendesde este entorno:

(entget(car(entsel))) aplicado a una LWpolyline en la líneade comandos de AutoCAD R14 devuelve:

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))

Obsérvese que las listas asociadas al código 10 contienen dosnúmeros reales además del código de asociación: (10 256.569 105.994) es decir, las coordenadas X e Y solamente. Sinembargo, al ejecutarlo desde la consola de Visual LISP aparece un tercernúmero real: (10 256.569 105.994 0.0), representando el valorde Z:

_$ (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))_$

Este comportamiento constituye un error que ya ha sido superado en laversión 2000.


Continúa...