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:

  • getdist - Prosi użytkownika o wskazanie odległości. Więcej szczegółów już wkrótce
  • progn - Tworzy blok kodu, który jest wykonywany, gdy wykonane ma być jedno polecenie. Na początku tej części napisałem że if wykonuje polecenie1 jeśli spełniony jest warunek, lub polecenie2 jeśli warunek nie jest spełniony. Zazwyczaj jednaj jest tak, że jeśli warunek jest spełniony wykonywana jest większa część kodu, więcej niż jedno polecenie. W takim przypadku musimy je zblokować. Gdyby tego nie zrobić i w if napisać linie kodu nie możemy liczyć że zostaną one wykonane, wykonana jest pierwsze polecenie jeśli warunek jest spełniony, drugie jest pominięte. Jeśli wpiszemy więcej niż 2 polecenia. w czasie działania programu wystąpi błąd. Jest to częsty błąd programistów (wiem po sobie ), przed czym przestrzegam.

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.
equalsprawdza, 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