Instrukcje warunkowe i pętle w LISP

Instrukcje warunkowe

if

If to podstawowa instrukcja warunkowa. Służy do podejmowania decyzji np. wykonywania kodu po zaistnieniu określonych warunków. Jej konstrukcja jest następująca

(if warunek
  (polecenie1)
  (polecenie2)
)

Jeśli warunek jest spełniony wykonywane jest polecenie1. Jeśli natomiast warunek nie jest spełniony, wykonywane jest polecenie2 Więcej o samych warunkach na końcu tej części

(setq SelDist (getdist))
(if (> SelDist 10)
  (progn
    (print "Wartość jest większa niż progowa")
    (setq OutVal SelDist )
  )
  (progn
    (print "Wskazałeś wartość mniejszą nież progowa")
    (setq OutVal 0)
  )
)
(print OutVal)
W powyższym przykładzie pojawiły się dwie nowe funkcje:

cond

Polecenie to jest podobne do polecenia if. Różnica jest taka, że można dzięki temu sprawdzić więcej przypadków.

(setq Ent (entget(car(entsel))))
(setq EntName (cdr(assoc 0 Ent)))
(cond
  ((or(= EntName "LIGHTWEIGHTPOLYLINE")(= EntName "POLYLINE")) (progn
    (print "Wybrałeś polilinię")
  ))
  ((= EntName "CIRCLE")(progn
    (print "Wybrałeś okrąg")
  ) )
  ((= EntName "ARC")
    (print "Wybrałeś łuk")
  )
  ((= EntName "LINE")
    (print "Wybrałeś linię")
  )
  (t
    (print "Wybrałeś coś innego")
  )
)

entget, entsel - użkownik wskazuje obiekt
assoc 0 - pobiera typ elementu z listy definicji.
cond wykonuje odpowiednią część kodu, w zależności od tego, który z warunków został spełniony. Czyli w naszym przypadku jaki jest typ wskazanego obiektu.
Warto tu zauważyć, że warunki są sprawdzane w kolejności ich wpisania, czyli jeśli zostanie spełniony np. 3. to najpierw sprawdzone jest 1 i 2 kryterium, jeśli nie są one spełnione, sprawdzone zostaje 3 kryterium. Ono jest spełnione więc wykonuje się to co jest w tej części, a pozostałe kryteria NIE są sprawdzane.

Pętle

Pętle są wykorzystywane kiedy potrzebne jest powtórzenie, często wielokrotne, tych samych lub bardzo podobnych poleceń. Rozróżniamy kilka różnych rodzajów pętli.

repeat

Funkcja repeat powtarza wykonanie polecenia określoną ilość razy Jeśli znana jest nam ilość powtórzeń Np. chcemy narysować gwiazdkę, możemy to zrobić tak:
(setq *ZCApp* (vlax-get-zwcad-object))
(setq *ActiveDocment* (vla-get-activedocument *ZCApp*))
(setq *MSpace* (vla-get-modelspace *ActiveDocment*))
(setq P0 (getpoint))
(setq i 0 R 10)
(repeat 12
  (setq P1 (polar P0 (* i (/ pi 6)) R))
  (vlax-invoke-method *MSpace* 'AddLine (vlax-3d-point P0) (vlax-3d-point P1))
  (setq i(1+ i))
)
Nowa funkcja, jaka nam się pojawiła to polar. Wyznacza ona współrzędne punktu względem punktu początkowego nachylonego do osi OX pod podanym kątem i w zadanej odległości od punktu początkowego (narysuję to innym razem).

while

Wykonuje polecenia tak długo, jak warunek jest spełniony. Dobrym przykładem teoretycznie jest odczytywanie pliku. Jeśli chcemy odczytać plik tekstowy, musimy odczytać znak po znaki lub linię po linii. zaczynając taki odczyt nie wiemy, jak duży jest plik, może on być pusty, mieć jedną linię, lub całe tysiące linii. Dlatego najłatwiej posłużyć się właśnie pętlą while i odczytywać tak długo, aż osiągnięty zostnie koniec pliku.
Posłużyć się też można innym przykładem, w którym użytkownik poproszony zostanie o wskazanie punktu. Jeśli to zrobi poproszony zostanie o wskazanie kolejnego punku, nasz program obliczy odległość pomiędzy tymi dwoma punktami i wyświetli ją i poprosi o wskazanie kolejnego punktu. wykonywało się to będzie tak długo, jak długo użytkownik będzie wskazywał punkt. Użytkownik może przerwać wskazywanie punktów na dwa sposoby: Przez [Enter] lub [ESC]. Jeśli użyje [Enter], getpoint zwróci nil to w łatwy sposób można obsłużyć. W przypadku przycisku [ESC] wyświetlony zostanie błąd. Obsługą błędów zajmiemy się już w niedługiej przyszłości.
(defun GetPoints ( / Px PointsList)
   (setq Px (getpoint "\nWskaż punkt"))
   (while Px
     (setq PointsList (append PointsList (list Px)))
     (setq Px (getpoint "\nWskaż punkt"))
   )
   PointsList
)

foreach

Wykonuje polecenia na każdym elemencie listy. Jest trochę podobna do mapcar, która też operuje na całych listach, jednak foreach daje większe możliwości. W przeciwieństwie do mapcar, gdzie można tylko jedną funkcję wykonać, foreach jest pętlą więc wewnątrz można wpisać dowolną ilość poleceń i posługiwać się też dodatkowymi zmiennymi, które nie są parametrami wywołania funkcji. Coś czuję, że sam się gubię w myślach, więc może jeden przykład za tysiąc słów.

W naszym przykładzie poprosimy użytkownika o wskazanie serii punktów, a następnie stworzymy polilinię. Zobrazuje to użycie pętli foreach, skożystamy również z pętli while zdefiniowanej wcześniej
(defun DrawLWPoly (Points / PtsLen PlineDef x LWPline OutLWPline )
   (setq PtsLen (length Points))
   (setq PlineDef (list '(0 . "LWPOLYLINE")'(100 . "AcDbEntity")'(100 . "AcDbPolyline")
       '(43 . 0)(cons 90 PtsLen)))
   (foreach x Points (setq PlineDef (append PlineDef (list (cons 10 x))))) ; W pętli dodajemy współrzędne punktów do definicji polilinii
     (setq LWPline (entmake PlineDef))
  )

(defun C:test ( / )
   (setq PtsLst (GetPoints))
   (DrawLWPoly PtsLst)
)

vlax-for

Wykonuje polecenia dla każdego elementu kolekcji.
Funkcja ta jest bliźniaczo podobna do funkcji foreach, z tą drobną różnicą, że nie operuje na listach a na kolekcjach. Kolekcje są czymś podobnym do list. Służą do grupowania obiektów danego typu. Jest kilka systemowych kolekcji i to właśnie na nich będziemy pracować. Najczęściej spotykane kolekcje to warstwy, typylini, bloki, grupy, rzutnie. Dostęp do nich mamy najłatwiejszy przez VisualLISP:

(setq Warstwy (vla-get-layers *ActiveDocment*))
(setq Bloki (vlax-get-property *ActiveDocment* 'Blocks ))

Dostęp do elementów kolekcji mamy na dwa sposoby. Podstawowy niewiele różni się od list. Możemy pobierać elementy indeksując je po numerze. Można to wykorzystać, jeśli chcemy wykonać jakąś operacje na każdym elemencie kolekcji. Nie jest tutaj potrzebna znajomość nazw np. warstw czy bloków.
Drugim sposobem dostępu do elementów kolekcji, jest indeksowany po nazwach elementów. Jest to najłatwiejszy sposób dostępu jeśli chcemy odczytać jakieś informacje a obiektu o znanej nam nazwie. A tak naprawdę, to oba sposoby sprowadzają się do jednego, a mianowicie do funkcji(vla-item Kolekcja Index) lub (vla-item Kolekcja "Nazwa")

(setq *ZCApp* (vlax-get-zwcad-object))
(setq *ActiveDocment* (vla-get-activedocument *ZCApp*))
(setq *MSpace* (vla-get-modelspace *ActiveDocment*))
(setq Warstwy (vla-get-layers *ActiveDocment*))
(setq War0 (vla-item Warstwy "0"))
(setq War0Zamk(vlax-get-property War0 'Lock))
(if (= War0Zamk -1)
   (print (strcat(vlax-get-property War0 'Name) " jest warstwą zamknięta"))
)
Ale trochę odbiegam od tematu. Mieliśmy skupić się na pętlach. W przypadku kolekcji można użyć funkcji vlax-for, która wykonuje określone polecenie na wszystkich elementach kolekcji. Jest to naprawdę bardzo proste i myślę że przykład zobrazuje wszystko.
(setq Warstwy (vla-get-layers *ActiveDocment*))
(vlax-for Warstwa Warstwy
  (print (vla-get-name Warstwa))
)

Operatory porównania

Operatory służą określeniu warunków, czyli sprawdzaniu relacji.

null

Sprawdza nam, czy zmienna podana jako argument tej funkcji ma wartość czy jest nil.
(null zmienna) zwróci nam
t   - czyli logiczną wartość prawdy, jeśli zmienna będzie miała jakąkolwiek wartość
nil - czyli logiczna wartość fałsz, jeśli zmienna będzie miała wartość pustą, czyli nil
Przykład bez sensu, ale obrazuje o co chodzi

(setq Px(getpoint "Wskaż punkt")) ; kilkamy [enter]
(if (null Px)
   (print "Nie wskazałeś punktu")
)

=

Relacja równości służy sprawdzeniu czy dane elementy są sobie równe czyli np.

(setq a 5)
(setq b 5.0)
(= a b)
= zwróci nam t
Funkcja ta służy sprawdzaniu zarówno wartości liczbowych jak i tekstowych.
(= "test" "test") zwróci t wartości są równe (= "test" "TEST") zwróci nil ponieważ wartości te nie są sobie równe

not

Służy sprawdzeniu czy warunek NIE został spełniony więc jeśli warunek jest spełniony, not zwraca nil, jeśli warunek nie jest spełniony not zwraca t.

< >

Porównania. Służą sprawdzeniu która wartość jest mniejsza i większa

(setq a (getint "Wpisz wartość"))
(setq b (getint "Wpisz drugą wartość do porówania"))
(if (< a b)
   (print (strcat (itoa a) " jest mniejsze" ))
   (print (strcat (itoa b) " jest mniejsze" ))
)

eq equal

eq sprawdza, czy zmienne sprawdzane są tymi samymi. Nie wystarczy że są takie same. np.
equal sprawdza, czy dwa wyrażenia są równe, z możliwością określenia tolerancji w przypadku liczb.
(setq s1 (list 10 10 10))
(setq s2 (list 10 10 10))
(setq s3 s1)
(eq s1 s2) ; zwraca nil. mimo, że wartości są różne. jednak nie odnoszą się do tego samego symbolu
(eq s1 s3) ; zwraca t. symbole s3 to to samo co s1
(equal s1 s2) ; zwraca t ponieważ wartości są równe
(equal 2 2.02 0.1); zwróci t, ponieważ z tolerancją 0.1, liczby te są równe
(equal 2 2.02 0.01); zwróci nil, ponieważ z tolerancją 0.01, liczby nie są równe


Spis treści
Dalej - Interakcja z Użytkownikiem, wskazania

Copyright © 2000—2010 by Usługi Informatyczne SZANSA - Gabriela Ciszyńska-Matuszek.
Autoryzowany Dystrybutor programu ZWCAD w Polsce od 2005 roku. Wszelkie prawa zastrzeżone.