Co to jest UDF?

edytuj

UDF (z ang. User Defined Functions) w dosłownym tłumaczeniu oznacza funkcje zdefiniowane przez użytkownika.

Najczęściej pod tym znaczeniem rozumiemy zbiór funkcji napisanych w AutoIt i zawartych w oddzielnym pliku.

Z funkcji tych można korzystać w pisanych skryptach bez konieczności kopiowania ich kodu.

Służy do tego dyrektywa include (patrz rozdział: AutoIt/Dyrektywy i funkcje konfiguracyjne translatora).

Dyrektywa ta powoduje dołączenie do skryptu kodu z zewnętrznego pliku. Dołączenie to odbywa się nie w czasie edycji jako proste wklejenie tekstu, lecz w czasie wykonywania lub kompilacji skryptu.


Funkcje wbudowane i funkcje biblioteczne

edytuj

Translator AutoIt zawiera pewną ilość wbudowanych funkcji, z których możemy korzystać bezpośrednio.

Funkcji tych jest całkiem sporo (prawie wszystkie opisane w tym podręczniku należą do tej kategorii), ale z oczywistych względów nie mogą one obejmować wszystkich możliwych zastosowań.

Na szczęście istnieje w AutoIt prosty sposób na zwiększenie możliwości języka poprzez tworzenie i korzystanie z zewnętrznych bibliotek funkcji.

Pokaźna standardowa biblioteka funkcji dołączona jest do dystrybucji języka i po zainstalowaniu znajduje się w katalogu /Include.

Opis funkcji biblioteki standardowej, podobnie jak opis funkcji wbudowanych znajduje się w pliku pomocy AutoIt. Nazwy wszystkich funkcji bibliotecznych rozpoczynają się od podkreślenia (np.: _IsPressed).

Proszę zwrócić uwagę, że nie wszystkie pliki biblioteki standardowej są UDF'ami, niektóre zawierają definicje stałych globalnych (np. /AVIConstants). Nazw tych stałych można używać zamiast parametrów liczbowych, w niektórych funkcjach wbudowanych (po dołączeniu dyrektywą include, np.: include<AVIConstants>).

Istnieje także całe mnóstwo różnych innych bibliotek dostępnych w sieci (np.: do obsługi przeglądarki Firefox, drukarki itp.).


Zasady tworzenie bibliotek funkcji

edytuj

Aby stworzony przez nas UDF zasługiwała na miano porządnej biblioteki musimy przy jego tworzeniu zachować pewne reguły. Jeżeli chcemy naszą pracą podzielić się z innymi, to musimy zadbać by nasz kod był przejrzysty i łatwy do analizy.

Z doświadczenie wiadomo, że chaotycznie napisany kod, bez komentarzy, staje się szybko trudny do analizy nawet przez samego autora, a co dopiero przez osobę postronną.

Taki kod trudno zrozumieć, a co za tym idzie trudno ulepszyć, poprawić czy rozwinąć.

Poniżej kilka zasad jakich powinniśmy przestrzegać pisząc UDF, który będziemy chcieli upublicznić:

1. Biblioteki powinny być tematyczne, tzn. powinny zawierać funkcje w jakim stopniu pokrewne (np. biblioteka graficzna, matematyczna, multimedialna itp.)

2. Dokumentacja kodu powinna zawierać:

- opis biblioteki

- wykaz zdefiniowanych stałych i funkcji

- opis stałych i funkcji

- opis parametrów przekazywanych do funkcji (ile ich jest, jakiego typu wartości przekazują, które są obowiązkowe, a które opcjonalne)

- opis wartości zwracanej przez funkcję (co ona reprezentuje i jakiego jest typu)

- opis ewentualnych wartości zwracanych przez makra @error i @extended

2. Kod funkcji:

- obsługa błędów (makro @error)

- obsługa parametrów opcjonalnych i wartości domyślnych

- deklarowanie zmiennych używanych wewnątrz funkcji


Zalecenia dotyczące nazewnictwa zmiennych

edytuj

Aby łatwiej orientować w używanych w skrypcie zmiennych w tworzeniu ich nazw zaleca się stosować następujące reguły:

1. Po znaku $ stosować dodatkowy prefiks określający typ wartości przechowywanej przez zmienną:

Prefiks | Typ wartości   | Inicjalizacja (przykład)
---------------------------------------------------------
i       | liczbowa       | $iVar = 0
---------------------------------------------------------
a       | tablica        | $aVar = 0 or $aVar[0]
---------------------------------------------------------
s       | string         | $sVar = ""
---------------------------------------------------------
f       | logiczna       | $fVar = False lub $fVar = True
---------------------------------------------------------
b       | binarna        | $bVar = ""
---------------------------------------------------------
h       | uchwyt, ID     | $hVar = 0
---------------------------------------------------------
p       | wskaźnik       | $pVar = 0
---------------------------------------------------------
tag     | struktura      |
        | predefiniowana |
---------------------------------------------------------
t       | struktura      | $tVar = 0
---------------------------------------------------------
o       | obiekt         | $oVar = 0
---------------------------------------------------------
v       | nieokreślona   | $vVar = 0 or $vVar = ""
---------------------------------------------------------

2. Nazwy powinny kojarzyć się z działaniem lub obiektem, którego dotyczą.

I tak dla zmiennej przechowującej uchwyt do okna GUI zamiast np. nazwy $x2 użyjemy $hGUI.

Pamiętajmy, żeby jednak nie przesadzać, np. w liczniku pętli możemy spokojnie użyć zmiennej $i zamiast $iLicznik_petli.


Podział linii kodu

edytuj

W AutoIt nie ma możliwości pisania dwóch lub więcej instrukcji w jednym wierszu tekstu (jedynym znakiem rozdzielającym instrukcje jest znak końca wiersza). Można natomiast zapisać jedną instrukcję w wielu wierszach.

Jest to czasami niezbędne przy bardzo długich instrukcjach. Podzielenie ich na kilka wierszy może zdecydowanie poprawić przejrzystość i czytelność kodu.

Do podziału instrukcji na wiersze służy znak podkreślenia "_", który musi być poprzedzony spacją.

Długa instrukcja:

MsgBox(64,"Okno demonstracyjne wyświetlania bardzo długich wierszy tekstu", "To jest bardzo, ale to bardzo długi tekst do wyświetlenia w oknie MsgBox!" & @LF & "A to drugi wiersz wyświetlany w tym oknie.")

I jej przykładowy podział:

MsgBox _ ;można wewnątrz instrukcji wstawić komentarz
(64, _ 
"Okno demonstracyjne wyświetlania bardzo długich wierszy tekstu", _
"To jest bardzo, ale to bardzo długi tekst do wyświetlenia w oknie MsgBox!" & _
@LF & "A to drugi wiersz wyświetlany w tym oknie.")

Nie można w ten sposób dzielić stringów. Próba takiego podziału spowoduje błąd:

MsgBox(64, "Okno demonstracyjne wyświetlania bardzo długich wierszy tekstu", "To jest bardzo, _
ale to bardzo długi tekst do wyświetlenia w oknie MsgBox!" & _
@LF & "A to drugi wiersz wyświetlany w tym oknie.")

Trzeba to zrobić tak:

MsgBox(64, "Okno demonstracyjne wyświetlania bardzo długich wierszy tekstu", "To jest bardzo," & _
"ale to bardzo długi tekst do wyświetlenia w oknie MsgBox!" & _
@LF & "A to drugi wiersz wyświetlany w tym oknie.")

A oto inny przykład prawidłowego podziału instrukcji:

Dim $a="a", $b=0
If ($a="a" Or _
    $a="B" Or _
    $a="c" Or _
    $a="d") And _
    $b<>2 And _
    $b<>5 Then _
    MsgBox(64, "Test podziału instrukcji", _
               "Warunek został spełniony!")


Tworzymy prosty UDF

edytuj

Przed przystąpieniem do pisania UDF'u dobrze jest przyjrzeć się bibliotekom dostępnym w dystrybucji AutoIt'a. Są one napisane z zachowaniem pewnej standardowej formy, którą będziemy mogli wykorzystać.

Załóżmy, że chcemy stworzyć bibliotekę funkcji matematycznych, niedostępnych w interpreterze:

_max           - wartość maksymalna
_min           - wartość minimalna
_sign          - znak liczby
_am            - średnia arytmetyczna
_gm            - średnia geometryczna
_log10         - logarytm dziesiętny
_logA          - logarytm o dowolnej podstawie
_sinh          - sinus hiperboliczny
_cosh          - cosinus hiperboliczny
_QRoots        - pierwiastki równania kwadratowego
_factorial     - silnia
_combination   - kombinacja bez powtórzeń
_combinationWR - kombinacja z powtórzeniami
_variation     - wariacja bez powtórzeń
_variationWR   - wariacja z powtórzeniami
_permutation   - permutacja
_Bin           - zamiana liczby na string liczby dwójkowej
_BinToDec      - zamiana stringu liczby dwójkowej na liczbę dziesiętną

Ponieważ naszą pracę będziemy chcieli udostępnić całemu światu, nazwy funkcji wywodzą się z języka angielskiego i zwyczajowo rozpoczynają znakiem podkreślenia.

Podobnie opisy powinny być w języku angielskim lub dwujęzyczne. I nie zapomnijmy o obowiązkowym #include-once na początku kodu.

Oto początek naszego UDF'a:

#include-once

; #INDEX# =======================================================================
; Nazwa .........: Mathematic function
; AutoIt Version : 3.3.8.1
; Language ......: Polish
; Description ...: Dodatkowe funkcje matematyczne (Additional math functions)
; Author(s) .....: Wasta (2012)
; ===============================================================================

; #CURRENT# =====================================================================
;Const $_PI
;_max
;_min
;_sign
;_am
;_gm
;_log10
;_logA
;_sinh
;_cosh
;_QRoots
;_factorial
;_combination
;_combinationWR
;_variation
;_variationWR
;_permutation
;_Bin
;_BinToDec
; ===============================================================================
Global Const $_PI = 3.14159265358979323846264338328

; #FUNCTION# ====================================================================
; Name...........: _max
; Description ...: Większy z dwóch parametrów (The larger of the two parameters)
; Syntax.........: _max($iVar1, $iVar2)
; Parameters ....: Dwie liczby rzeczywiste (Two real numbers)
; Return values .: Większy z dwóch parametrów (The larger of the two parameters)
Func _max ($iVar1, $iVar2)
	If $iVar1>=$iVar2 Then Return $iVar1
	Return $iVar2
EndFunc

; ...
; ...
; ...  itd.

UWAGA: W edytorze SciTe rozbudowany nagłówek funkcji możemy uzyskać wciskając kombinację klawiszy Ctrl+Alt+H (kursor musi znajdować się w wierszu z początkiem definicji funkcji).

Dalszy ciąg w ramach ćwiczeń.

Jak nieczytelny może być kod w AutoIt?

edytuj

Mały przykład użycia zdefiniowanej powyżej funkcji _max, (skrypt wypisuje większą z podanych liczb):

Dim $a=7, $b=2

MsgBox(64, "Liczba większa", _max($a,$b))

Func _max ($iVar1, $iVar2)
	If $iVar1>=$iVar2 Then Return $iVar1
	Return $iVar2
EndFunc

A teraz całkowicie równoważna mu wersja mało czytelna (wygląda jakby małpa tłukła w klawiaturę, ale działa!):

Dim $a=7, $b=2 ;wprowadzenie danych jak w poprzednim przykładzie, ale dalej to już trochę trudniej

$____ _;_ ___$
        = _
msgbOX-(1-1==1=1)
$______ _
	= _
biNarytOSTrinG&"biNarytOSTrinG"
$___ _;_ __$
=( _;_)=
1--(1=1=1))^(1-- _
(1=1=1))^(1--(1= _
	1)--(1=1=1)) _
/(-((1=1-1)-1))
$_____= _
$______(0x20617a736bea6957)&$______(0x202061627a63696c)
	$_=( _
$a/((1=1)--1))- _
	-($b/(1--(--1=1) _
))
	fUnC _ _
($_,$__)
$_-=-1-1=--1
retuRN _
--$_--$__>--$_---$__?--$_----$__:----$_-----$__
	enDfunC
$__=($a/((1==1)--1))-($b/(1--(--1=1)))
$____($___, $_____, _($_,$__))

Tego typu techniki zaciemniania kodu są czasem stosowane celem jego ochrony przed wykorzystaniem przez osoby niepowołane. Metoda ta nosi nazwę obfuskacji (z ang. obfuscation). Nie uniemożliwia ona wprawdzie całkowicie analizy kody, ale znacznie ją utrudnia (co widać na powyższym przykładzie).

Zalecenia dotyczące przejrzystego programowania

edytuj

Na koniec kilka zaleceń, aby nasz kod był elegancki i łatwy do analizowania:

Czytelna struktura programu, kolejno:

1. Dyrektywy Include.
2. Inne dyrektywy i funkcje konfiguracyjne translatora.
3. Definicje skrótów klawiszowych, jeżeli mają zasięg globalny.
4. Deklaracje zmiennych i stałych globalnych i lokalnych segmentu głównego
5. Kod segmentu głównego.
6. Kod definicji funkcji w jakiejś rozsądnej kolejności (na początku te najważniejsze). 
   Jeżeli funkcja A w definicji wywołuje funkcję B, to logicznym jest, 
   że definicja  funkcji A powinna być przed definicją funkcji B.

Jak pisać kod i czego unikać:

1. Stosować wcięcia w pętlach, instrukcjach warunkowych i definicjach funkcji. 
   Dzięki temu łatwo będzie rozpoznać zakres tej konstrukcji.
   Szczególnie jest to pomocne przy wielokrotnych zagłębieniach.
2. Nazwy zmiennych i funkcji powinny logicznie kojarzyć się z obiektem lub działaniem, którego dotyczą.
3. Przy tworzeniu GUI grupować instrukcje dotyczące jednego obiektu, np. kontrolki czy okna.
   Taką grupę można wydzielić za pomocą pustych wierszy przed i za nią.
4. Nie wstawiać po każdej linii kodu pustych wierszy. 
   Puste wiersze powinny jedynie podkreślać pewną odrębność danego fragmentu kodu.
5. Komentować trudniejsze i ważniejsze fragmenty kodu.
6. Stosować prefiksy nazw zmiennych określające typ wartości jaką zawiera taka zmienna. 

Ćwiczenia

edytuj

1. Napisać pozostałą część biblioteki matematycznej, oraz prosty skrypt testujący jej działanie.

Przykładowe rozwiązania: AutoIt/Ćwiczenia dla zaawansowanych - przykładowe rozwiązania