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


El programa expuesto en el capítulo sobre los procedimientos recusivos, ha sido elaborado utilizando sólo las funciones primitivas conocidas entonces de AutoLISP. Una manera más eficaz de encarar su análisis sería la de tener en cuenta si algunos de los procesos que se llevan a cabo dentro del mismo pudieran programarse independientemente, como funciones utilitarias. Una función utilitaria es un nuevo operador que añadimos al lenguaje de programación para resolver situaciones que pueden presentarse con cierta frecuencia dentro de nuestros programas. En el caso que estamos analizando, podemos concebir la necesidad de una nueva función que recorra una lista y de acuerdo con el resultado de una función que se le pase como predicado elimine unos términos y conserve otros. Como resultado tendríamos una lista que sólo incluyera los términos deseados, en este caso las listas de asociación identificadas con los códigos 10. Estas funciones que pudiéramos llamar QUITAR-SI y su complementaria QUITAR-SI-NO han estado siempre entre las primeras que los programadores LISP han añadido a su repertorio de utilidades. Una definició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 de Common 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í las encontraremos en el catálogo de funciones Visual LISP como VL-REMOVE-IF y VL-REMOVE-IF-NOT.

Utilizando VL-REMOVE-IF-NOT y con la ayuda de la función de mapeado sobre listas MAPCAR, estudiada en el apartado anterior, nuestra funció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 la compilació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ás económica en cuanto a esfuerzo de programación. Por otra parte, supera las limitaciones de Visual LISP en cuanto a la recursión que pudiera provocar en caso de polilíneas con un gran número de vértices un error por desbordamiento de pila.

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

(entget(car(entsel))) aplicado a una LWpolyline en la línea de 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 dos números reales además del código de asociación: (10 256.569 105.994) es decir, las coordenadas X e Y solamente. Sin embargo, al ejecutarlo desde la consola de Visual LISP aparece un tercer número real: (10 256.569 105.994 0.0), representando el valor de 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 la versión 2000.


Inicio | Índice | Continúa...

Comments