Średniej ruchomej cpan
W tym rozdziale przedstawiono koncepcje dotyczące odnośników do modułów, pakietów i klas Perla. Pokazuje również, jak utworzyć kilka modułów przykładowych. Moduł Perla to zestaw kodu Perl, który działa jak biblioteka funkcji. Termin modułu Perl jest synonimem pakietu tekstowego Pakiety są cechą Perl 4, podczas gdy moduły są rozpowszechnione w Perl 5. Możesz zachować cały swój wielokrotny kod Perla do zestawu zadań w module Perl Dlatego wszystkie funkcje dotyczące jednego typu zadanie jest zawarte w jednym pliku Łatwiej jest zbudować aplikację na tych modułowych blokach W związku z tym, moduł tekstowy ma nieco więcej niż pakiet. Oto krótkie wprowadzenie do modułów Niektóre tematy z tej sekcji zostaną szczegółowo omówione przez resztę książka Przeczytaj uważnie poniższe akapity, aby uzyskać informacje na temat tego, co spotyka się podczas pisania i używania własnych modułów. Co mylące jest to, że moduł i pakiet warunków są używane zamiennie we wszystkich dokumentach Perla a te dwa terminy oznaczają to samo Więc podczas czytania dokumentów Perla, po prostu pomyśl pakiet, gdy zobaczysz moduł i vice versa. So, co jest założeniem używania modułów Cóż, moduły są tam, aby pakiet przebłagać zmiennych typu Pun, symboli i wzajemnie połączonych elementów danych Na przykład, przy użyciu zmiennych globalnych o bardzo popularnych nazwach, takich jak kj lub i w programie, ogólnie nie jest dobrym pomysłem Również licznik pętli, powinienem być dozwolony do pracy niezależnie w dwóch różnych fragmentach kodu Deklarowanie i jako zmiennej globalnej, a następnie zwiększanie jej z poziomu podprogramu spowoduje, że utracone problemy z kodem aplikacji powodują, że podprogram mógł być wywołany z pętli, która również korzysta ze zmiennej o nazwie i Użycie modułów w Perl umożliwia zmienne ta sama nazwa, która ma zostać utworzona w różnych, różnych miejscach w tym samym programie. Symbole zdefiniowane dla zmiennych są przechowywane w tablicy asocjacyjnej, określanej jako tablica symboli Te tabele symboli s są unikatowe dla pakietu Dlatego zmienne o tej samej nazwie w dwóch różnych pakietach mogą mieć różne wartości. Mamy moduł posiada własną tabelę symboli wszystkich symboli zadeklarowanych wewnątrz niej Tabela symboli w zasadzie izoluje nazwy synonimów w jednym module od drugiego tabela symboli definiuje przestrzeń nazw, czyli przestrzeń dla niezależnych nazw zmiennych w taki sposób, że użycie modułów, każdy z własną tabelą symboli, uniemożliwia zadeklarowanie zmiennej zadeklarowanej w jednej sekcji przez zastąpienie innych zmiennych o tej samej nazwie zadeklarowanej gdzie indziej w tym samym programie. W rzeczywistości wszystkie zmienne w Perl należą do pakietu Zmienne w programie Perl należą do pakietu głównego Wszystkie inne pakiety w ramach programu Perl są zagnieżdżone w tym głównym pakiecie lub istnieją w tym samym poziom Istnieją pewne naprawdę globalne zmienne, takie jak tablica SIG obsługi sygnału, które są dostępne dla wszystkich innych modułów w programie aplikacyjnym i nie mogą być izolowane za pośrednictwem przestrzeni nazw ose zmienne identyfikatory zaczynające się od liter lub podkreślenia są przechowywane w tablicy symboli modułu Wszystkie pozostałe symbole, na przykład nazwy STDIN STDOUT STDERR ARGV ARGVOUT ENV Inc i SIG, muszą być umieszczone w pakiecie main. Switching między pakietami dotyczy tylko obszarów nazw Wszystko robią to, gdy używasz jednego pakietu lub innego deklaruje, która tabela symboli ma być używana jako domyślna tablica symboli do wyszukiwania nazw zmiennych Tylko zmienne dynamiczne mają wpływ na użycie tabel symboli Zmienne zadeklarowane przez użycie słowa kluczowego są nadal rozwiązywane z bloku kodu, w którym się znajdują, i nie są odwoływane przez tabele symboli W rzeczywistości zakres deklaracji pakietowej pozostaje aktywny tylko w bloku kodu zadeklarowanym w ten sposób. Jeśli przełączasz tabele symboli, używając pakietu wewnątrz podprogramu, oryginalna tabela symboli będąca efektem wywołania połączenia zostanie przywrócona po powrocie podprogramu. Przełączanie tabel symboli ma wpływ tylko na domyślne wyszukiwanie dynamicznych varia Można też wyraźnie odwoływać się do zmiennych, uchwytów plików itd. w określonym pakiecie, przedkładając pakietName do nazwy zmiennej. Widziałaś, jaki był kontekst pakietu, gdy używasz odnośników w Rozdziale 3 Kontekst pakietów oznacza po prostu użycie tabela symboli przez interpreter języka Perl do rozwiązywania nazw zmiennych w programie Poprzez przełączanie tabel symboli zmieniamy kontekst pakietu. Moduły mogą być zagnieżdżone w innych modułach Zagnieżdżony moduł może używać zmiennych i funkcji modułu, który jest zagnieżdżony wewnątrz W przypadku zagnieżdżonych moduły musiałyby używać modułuName nestedModuleName i tak dalej Użycie podwójnego dwukropka jest synonimem przy użyciu cytatu wstecznego Jednak podwójny dwukropek jest preferowanym, przyszłym sposobem adresowania zmiennych wewnątrz modułów. Niezwykłe adresowanie zmiennych modułu zawsze odbywa się za pomocą pełne odniesienie Na przykład, załóżmy, że masz moduł, Inwestycje, które jest domyślnym pakietem w użyciu, a chcesz zająć się innym modułem, Obligacje, które jest zagnieżdżony w module inwestycyjnym W tym przypadku nie można używać obligacji zamiennych, należy użyć obligacji inwestycyjnych do adresowania zmiennych i funkcji w module obligacji. Zastosowanie obligacji oznaczałoby użycie pakietu obligacji, który jest zagnieżdżony w głównym module i nie w module inwestycyjnym. Tabela symboli modułu jest rzeczywiście przechowywana w tablicy asocjacyjnej nazw modułów dołączonych do dwukropek Tabela symboli modułu o nazwie Bond będzie określana jako tablica asocjacyjna Obligacja Nazwa symbolu tabela głównego modułu jest główna i może być nawet skrócona do Podobnie, wszystkie zagnieżdżone pakiety mają swoje symbole zapisane w tablicach asocjacyjnych z podwójnymi dwukropkami oddzielającymi każdy poziom zagnieżdżania. Na przykład, w module Bond, który jest zagnieżdżony w module Inwestycyjnym, tablica asocjacyjna dla symboli w module Bond będzie nazywana inwestycyjna obligacja. Typeglob jest naprawdę globalnym typem dla nazwy symbolu Można wykonać operacje aliasingowe, przypisując do typeglob W tablicy asocjacyjnej dla symboli zostanie użyty jeden lub więcej wpisów w tablicy asocjacyjnej symboli, przy czym przyporządkowanie następuje za pomocą typeglob. Rzeczywistą wartością w każdym wpisie tablicy asocjacyjnej jest to, o czym mówisz, gdy używasz notacji zmiennejNastępnie istnieją dwa sposoby odnoszące się do zmiennych nazw w pakiecie Inwestycyjne rachunki inwestycyjne. W pierwszej metodzie odwołujesz się do zmiennych poprzez odniesienie typeglob Użycie tabeli symboli Inwestycja jest tutaj sugerowana, a Perl zoptymalizuje wyszukiwanie symboli pieniędzy i rachunki Jest to szybszy i preferowany sposób adresowania symbolu Druga metoda polega na wyszukiwaniu wartości zmiennej adresowanej przez pieniądze i rachunki w tablicy asocjacyjnej używanej do symboli, Inwestowanie wyraźnie To wyszukiwanie zostanie wykonane dynamicznie i nie zostanie zoptymalizowane przez Perl Dlatego wyszukiwanie będzie zmuszone do sprawdzenia tablicy asocjacyjnej za każdym razem, gdy instrukcja zostanie wykonana W rezultacie druga metoda nie jest wydajna i powinna być używana tylko do demonstracji w jaki sposób tablica symboli jest implementowana wewnętrznie. Innym przykładem w tej deklaracji jest kamran husain. causes zmiennych, podprogramów i uchwytów plików, które są oznaczone za pomocą symbolu kamran, a także adresowane za pośrednictwem symbolu husain Oznacza to, że wszystkie wpisy symboli w bieżącej tablicy symboli z kluczem kamran zawiera teraz odniesienia do tych symboli adresowanych przez klucz husain Aby zapobiec takiemu globalnemu przypisaniu, można użyć wyraźnych odniesień Na przykład poniższe stwierdzenie umożliwi adresowanie zawartości pliku husain przez zmienną kamran kamran husain. Jednak wszystkie tablice takie jak kamran i husain nie będą takie same Jak tylko konkretne odnośniki zostaną jednoznacznie zmienione Aby podsumować, przy przypisywaniu jednej typeglob do innej, wpływasz na wszystkie wpisy w tabeli symboli niezależnie od typu zmiennej o której mowa Kiedy przypisujesz odniesienie z jednego typu zmiennego do innego, wpływasz tylko na jeden wpis w tabeli symboli. A moduł Perl plik ma następującą formułę formatu: ModuleName Wstaw kod modułu 1. Nazwa pliku musi być nazwana Nazwa modułu musi kończyć się ciągiem przez konwencję Instrukcja pakietu jest pierwszą linią pliku Ostatnim wierszem pliku musi być wiersz z instrukcją 1 W efekcie zwraca wartość true do programu aplikacyjnego za pomocą modułu Nie używając instrukcji 1 nie pozwala na prawidłowe załadowanie modułu. Instrukcja pakietu mówi tłumaczowi Perl, aby rozpocząć nową domenę nazw obszaru nazw. zmienne w skrypcie Perl należą do pakietu o nazwie main Każda zmienna w głównym pakiecie może być określana jako główna zmienna. Oto składnia tego typu referencji nazwa_pliku nazwa_zestawu. Jednak pojedynczy cytat jest synonimem operatora podwójnego okręgu obejmującego więcej zastosowań operator w następnym rozdziale Na razie musimy pamiętać, że następujące dwa zestawienia są równoważne packageName variableName packageName variableName. Dwójka dwukropek ntax jest uważany za standardowy w świecie Perl W związku z tym, aby zachować czytelność, używam składni dwukropek w dalszej części tej książki, chyba że absolutnie konieczne jest wprowadzenie wyjątków, aby udowodnić punkt. Domyślne użycie zmiennej nazwy powoduje bieżący pakiet aktywny w czasie kompilacji W ten sposób, jeśli znajdujesz się w pakiecie i określ zmienną pv, to zmienna jest w rzeczywistości równa finansowi pv. Using Perl Modules używają vs require. You zawierają moduły Perl w swoim programie przy użyciu lub require instrukcja Oto sposób używania któregokolwiek z tych stwierdzeń użyj ModuleName wymaga ModuleName. Zauważ, że rozszerzenie nie jest używane w kodzie wskazanym powyżej Również, że żadna instrukcja nie pozwala na więcej niż jeden plik w programie Zwracana wartość true 1 jako ostatnia instrukcja jest wymagana, aby Perl wiedział, że wymagany moduł d lub moduł d ładowany poprawnie i umożliwia interpreter Perl ignoruje wszelkie ładowania Ogólnie rzecz biorąc, lepiej używać instrukcji modułu use than th e wymagać, aby instrukcja modułu w programie Perl pozostała kompatybilna z przyszłymi wersjami modułów Perl. For, warto rozważyć kontynuowanie użycia instrukcji wymagań Poniżej znajduje się instrukcja użycia, która działa nieco bardziej niż wymaga tego instrukcja, zmienia przestrzeń nazwową modułu, który zawiera inny moduł Chcesz tę dodatkową aktualizację przestrzeni nazw do zrobienia w programie Jednak podczas pisania kodu modułu może nie być konieczne zmienianie obszaru nazw, chyba że jest to wyraźnie wymagane W tym wydarzeniu , użyjesz instrukcji require. Instrukcja require zawiera pełną nazwę ścieżki pliku w tablicy Inc, tak aby funkcje i zmienne w module s znajdowały się w znanej lokalizacji podczas czasu wykonania. Dlatego funkcje importowane z pliku moduł jest importowany za pośrednictwem jawnego odniesienia modułu w czasie wykonywania z instrukcją require Instrukcja use wykonuje to samo co żądanie require, ponieważ aktualizuje tablicę Inc z pełną ścieżką nazwy załadowanych modułów Kod funkcji funkcji również idzie o krok dalej i wywołuje funkcję importu w module używającym d, aby wyraźnie załadować listę wyeksportowanych funkcji w czasie kompilacji, oszczędzając czas wymagany do wyraźnej rozdzielczości funkcji nazwa podczas wykonywania. Zasadniczo instrukcja use jest równoważne z wymaganiem importu ModuleName Nazwa modułuName lista importowanych funkcji. Korzystanie z instrukcji use powoduje zmianę obszaru nazw programu s, ponieważ importowane nazwy funkcji są wstawiane do tabeli symboli. Instrukcja require nie zmienia obszar nazw swojego programu W związku z tym następujące oświadczenie użyj ModuleName. is jest równoważne tej deklaracji wymaga ModuleName. Functions są importowane z modułu przez wywołanie funkcji zwanej importem Możesz napisać własną funkcję importu w module, lub możesz użyć Moduł Eksportera i użyj jego funkcji importu W niemal wszystkich przypadkach moduł Exporter wykorzystuje moduł dostarczający funkcję importowania, a nie rewolucyjny Koło Nauczysz się więcej na ten temat w następnej sekcji Jeśli zdecydujesz się nie używać modułu Exporter, będziesz musiał napisać własną funkcję importowania w każdym module, który piszesz Jest o wiele łatwiej po prostu korzystać z modułu Exporter i pozwól Perl wykonaj pracę dla Ciebie. Przykładowy moduł. Najlepszym sposobem na zilustrowanie semantyki sposobu używania modułu w programie Perl jest napisanie prostego modułu i pokazanie, jak go używać. Przyjmijmy przykład lokalnego kredytu, Rudious Maximus , który jest po prostu zmęczony wpisywaniem tej samej prośby o listy zapłat Jest zapalonym fanem komputerów i Perl, Rudious bierze leniwy programista podejście i pisze moduł Perl, aby pomóc mu wygenerować notatki i listy. Teraz zamiast wpisywać w polach w pliku szablonu notatki wystarczy wpisać kilka wierszy, aby utworzyć ładną, groźną notatkę. Listing 4 1 pokazuje, co ma wpisać listing 4 1 Korzystanie z modułu Letter 1 usr bin perl - w 2 3 Odinstaluj poniżej, aby dodać bieżący katalog w Inc 4 push Inc , pwd 5 6 use Letter 7 8 List do Mr Gambling Man, Pieniądze dla Szczęśliwego Psa, Wyścig 2 9 List ClaimMoneyNice 10 List ThankDem 11 Finish Letter. Użycie instrukcji Letter jest obecne, aby zmusić interpretera Perl do dołączenia kodu modułu w programie aplikacyjnym Moduł powinien znajdować się w katalogu usr lib perl5 lub można go umieścić w dowolnym katalogu wymienionym w tablicy Inc Tablica Inc to lista katalogów, które Perl interpreter będzie szukać podczas próby załadowania kodu dla wymienionego modułu Skomentowany numer linii 4 pokazuje jak dodać bieżący katalog roboczy do ścieżki Następujące cztery linie w pliku wygenerują temat literki. Oto dane wyjściowe z używania modułu literowego Do Mr Gambling Man Fm Rudious Maximus, kredyt Shark Dt śr. 7 lutego 10 35 51 CST 1996.Re Pieniądze na Lucky Dog, wyścig 2. Przypuszczam, że Twoje konto się skończyło Musisz nas zapłacić Czy chcesz nam przyjechać? ovah. Thanks dla Ciebie support. Plik modułu Letter jest pokazany w listingu 4 2 Nazwa pakietu jest zadeklarowana w pierwszej linijce Ponieważ funkcje tego modułu zostaną wyeksportowane, używam modułu Exporter. W związku z tym instrukcja użyj Eksportera jest potrzebna do dziedziczenia funkcji z poziomu Moduł eksportera Kolejnym wymaganym krokiem jest umieszczenie słowa Eksportowane w tablicy ISA, aby umożliwić przeszukiwanie. Tablica ISA jest specjalną tablicą w obrębie każdej paczki Każda pozycja w tablicy wymienia gdzie indziej szukać metody, jeśli nie można jej znaleźć w bieżącym pakiet Kolejność, w jakiej znajdują się pakiety zawarte w tablicy ISA to kolejność, w jakiej Perl wyszukuje nierozwiązane symbole Klasa, która jest wymieniona w tablicy ISA jest określana jako klasa bazowa danej klasy Perl zbierze brakujące metody znalezione w bazie klasy do przyszłych modyfikacji Modyfikacja tablicy ISA spowoduje spuszczenie pamięci podręcznej i spowoduje, że Perl ponownie wyszuka wszystkie metody. Teraz spójrz na kod z listingu 4 2 Listing 4 2 Pakiet modułu 1 List 2 3 wymagają eksportera 4 Eksporter ISA 5 6 główka 1 NAZWA 7 8 List - moduł próbki do wygenerowania nagłówka dla Ciebie 9 10 head1 SKŁADNIA 11 12 użycie List 13 14 List Data 15 List Aby nazwa, firma, adres 16 17 Następnie jeden z następujących 18 List ClaimMoneyNice 19 List ClaimMoney 20 Letter ThreatBreakLeg 21 22 List ThankDem 23 Wykończenie listu 24 25 head1 OPIS 26 27 Ten moduł stanowi krótki przykład wygenerowania listu 28 przyjaznego rekina pożyczki z pożywką 29 30 Kod zaczyna się po cut cut statement 31 cut 32 33 EKSPORT qw Data, 34 To, 35 ClaimMoney, 36 ClaimMoneyNice, 37 ThankDem, 38 Finish 39 40 41 Wydrukuj bieżącą datę 42 43 sub Letter Data 44 data daty 45 print n Dzisiaj jest data 46 47 48 sub List Do 49 local shift name 50 lokalna zmiana przedmiotu 51 print n Aby nazwa 52 print n Fm Rudious Maximus, Kredyt Shark 53 print n Dt, data 54 print n Temat 55 drukowanie nn 56 drukowanie nn 57 58 sub Letter ClaimMoney 59 drukuj n Masz mi pieniądze Złóż swój akt razem 60 wydruków n Czy chcesz, żebym wysłał Bruno do 61 druków n zebrać je lub zamierzasz zapłacić 62 63 64 sub Letter ClaimMoneyNice 65 drukuj n Przypomnijmy, że Twoje konto jest wydrukowane na naciśniętym klawiszu 67 print n You zapłacimy nam wkrótce 68 drukuj n lub czy chcesz bym przyjechał ovah 69 70 71 sub Letter ThreatBreakLeg 72 print n najwyraźniej listy takie jak te nie pomagaj 73 drukuj n będę musiał zrobić przykład dla Ciebie 74 drukuj nn Zobacz się w szpitalu , pal 75 76 77 sub List ThankDem 78 print nn Dziękujemy za wsparcie 79 80 81 sub Finish Letter 82 printf nnnn Z poważaniem 83 printf n Rudious n 84 85 86 1.Linie zawierające ten sam znak są używane do dokumentacji Musisz dokumentować każdy moduł dla własne moduły referencyjne Perl nie muszą być udokumentowane, ale dobrze jest napisać kilka wierszy o tym, co robi Twój kod Kilka lat od teraz możesz zapomnieć, co to jest moduł O dobrej dokumentacji jest zawsze koniecznością, jeśli chcesz pamiętam to, co zrobiłeś w przeszłości style dokumentacji używane dla Perl w rozdziale 8 Dokumentowanie skryptów Perla W tym module próbki instrukcja head1 rozpoczyna dokumentację Wszystko co do instrukcji cut jest ignorowane przez interpreter Perl. Następnie moduł wyszczególnia wszystkie funkcje wywożone przez ten moduł w pliku EXPORT tablica EXPORT definiuje wszystkie nazwy funkcji, które mogą być wywoływane przez kod zewnętrzny Jeśli nie została wyświetlona lista funkcji w tej tablicy EXPORT, nie została ona wyświetlona przez zewnętrzne moduły kodu. Poślaniem tablicy EXPORT jest ciało kodu, podprocedura w czasie Po zdefiniowaniu wszystkich podprogramów końcowe stwierdzenie 1 kończy się plikiem modułu 1 musi być ostatnia linia wykonywalna w pliku. Pozwól na niektóre funkcje zdefiniowane w tym module Pierwsza funkcja, na którą się zwracamy, to prosta funkcja Date, wiersze 43 do 46, która drukuje bieżącą datę i czas UNIX Brak tej funkcji i nie zwraca niczego znaczącego z powrotem do wywołującego. Zwróć uwagę na użycie mojego przed datą var iable w wierszu 44 Moje słowo kluczowe używa się do ograniczenia zakresu zmiennej do wewnątrz klamerki nawiasów krzywych funkcji Date Kod między nawiasami klamrowymi nazywane są zmiennymi blokowymi zadeklarowanymi w obrębie bloku są ograniczone zakresem do wewnątrz nawiasów klamrowych W 49 i 50, lokalna nazwa zmiennej i podmiot są widoczne dla wszystkich funkcji. Można również zadeklarować zmienne z lokalnym kwalifikatorem Użycie lokalnego pozwala na zmianę w zakresie bieżącego bloku oraz innych bloków kodu wywołanych z wewnątrz ten blok W ten sposób lokalny x zadeklarowany w obrębie jednego bloku jest widoczny dla wszystkich kolejnych bloków wywołanych z tego bloku i może być odwołany W poniższym przykładzie kod można uzyskać dostęp do zmiennej nazw funkcji ToTitled, ale nie na danych podanej w 1 phonie iphone 1 ToTitled 2 zmiana nazwy lokalnej 3 mój telefon shift. przy przykładowy kod pokazał, jak wyodrębnić jeden parametr na raz Podprogram Aby wziąć dwa parametry, aby skonfigurować nagłówek dla memo. Używanie funkcji w module to n ot różni się od używania i definiowania modułów Perl w obrębie tego samego pliku kodu Parametry są przekazywane przez odniesienie, o ile nie określono inaczej Wielokrotne tablice podane w podprogramie, jeśli nie wyraźnie dereferencjonowane przy użyciu odwrotnego ukośnika, są łączone. Tablica wejściowa w funkcji zawsze jest tablicą wartości skalarnych Przesyłanie wartości przez odniesienie jest preferowanym sposobem w Perl, aby przekazać dużą ilość danych do podprogramu Zobacz rozdział 3 Materiały referencyjne. Nowy przykładowy moduł finansów. Moduł finansowy, pokazany na listingu 4 3, służy do prostego obliczania dla wartości pożyczek Wykorzystanie modułu Finansowania jest proste Wszystkie funkcje są zapisywane z tymi samymi parametrami, jak pokazano w formule dla funkcji. Spójrzmy na to, jak można obliczyć przyszłą wartość inwestycji Przykładowo, jeśli zainwestujesz kilka dolarów, pv w obligacji, która oferuje stałą stopę procentową, r stosowaną w znanych przedziałach czasowych dla n okresów, jaka jest wartość obligacji w momencie jej wygaśnięcia W tym przypadku se, będziesz używać następującej formuły fv pv 1 r n. Funkcja, aby uzyskać przyszłą wartość, jest zadeklarowana jako FutureValue Patrz Listing 4 3, aby zobaczyć, jak go używać Listing 4 3 Korzystanie z modułu Finance 1 usr bin perl - w 2 3 push Inc, pwd 4 use Finanse 5 6 pożyczki 5000 00 7 apr 3 5 kwietnia 8 lat 10 lat 9 10 ------------------------ ---------------------------------------- 11 Obliczyć wartość po zakończeniu pożyczki jeśli odsetki 12 stosuje się co roku 13 ------------------------------------------ ---------------------- 14 rok czasowy 15 fv1 Finanse Przyszłość kredytu future przedterminowo 16 print n Jeśli odsetki są stosowane na koniec roku 17 drukuj przyszłość wartość pożyczki pożyczki n 18 drukuj według APR z, apr dla, czasu, lat 19 printf is 8 2f n fv1 20 21 --------------------- ------------------------------------------- 22 Obliczyć wartość na końcu kredytu, jeśli odsetki 23 stosuje się co miesiąc 24 --------------------------------------- ------------------------- 25 stawka apr 12 kwietnia 26 czasu tak r 12 w miesiącach 27 fv2 Finanse Przyszłość kredytu, stopa procentowa, czas 28 29 drukuj n Jeżeli odsetki są stosowane na koniec każdego miesiąca 30 print n przyszła wartość pożyczki n 31 wydrukowana w kwietniu, miesiące 32 printf jest 8 2f n fv2 33 34 printf n Różnica wartości wynosi 8 2f, fv2 - fv1 35 printf n Dlatego przez zastosowanie odsetek w krótszych okresach 36 printf n w rzeczywistości otrzymujemy więcej pieniędzy w odsetkach n. Oto próbka dane wejściowe i wyjściowe z listy 4 3 testme. If odsetki są stosowane na koniec roku Przyszła wartość pożyczki w wysokości 5000 na poziomie 3RR za okres 10 lat wynosi 7052 99. Jeśli odsetki są stosowane na koniec każdego miesiąca Przyszła wartość za pożyczkę w wysokości 5000 w kwietniu 3 5 za 120 miesięcy wynosi 7091 72. Różnica w wartości wynosi 38 73 W związku z tym, stosując odsetki w krótszych okresach, uzyskujemy więcej pieniędzy z odsetkami. Ujawnienie w wyniku jest wynikiem porównania wartości pomiędzy fv1 i fv2 Wartość fv1 oblicza się przy użyciu i co najmniej raz w roku w okresie obowiązywania obligacji fv2 jest wartością, jeśli odsetki są stosowane co miesiąc przy równoważnej miesięcznej stopie procentowej. Pakiet jest pokazany na listingu 4 4 na jego wczesnych etapach rozwoju. Listing 4 4 Pakiet 1 pakiet Finanse 2 3 wymagają eksportera 4 ISA Exporter 5 6 head1 7 8 Kalkulator Finansowy - łatwe obliczenia finansowe przy użyciu Perl 9 10 head 2 11 użyj Finanse 12 13 pv 10000 0 14 15 stawki 12 5 12 APR miesięcznie 16 17 czasu 360 miesięcy na pożyczkę na dojrzałość 18 19 fv FutureValue 20 21 drukuj fv 22 23 cięcie 24 25 EKSPORT qw FutureValue, 26 PresentValue, 27 FVofAnnuity, 28 AnnuityOfFV, 29 getLastAverage, 30 getMovingAverage, 31 SetInterest 32 33 34 Globals, jeśli dotyczy 35 36 37 lokalne defaultInterest 5 0 38 39 sub Finance SetInterest 40 moja zmiana stopy 41 domyślnieInterest rate 42 printf n defaultInterest rate 43 44 45 -------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------ 46 Uwagi 47 1 Oprocentowanie r jest podane w wartości 0-100 48 2 Wartość n podana w warunki to stopa, w której stosuje się odsetki 49 50 51 ------------------------------------ -------------------------------- 52 53 ---------------- -------------------------------------------------- - 54 Wartość bieżąca inwestycji 55 fv - wartość przyszła 56 r - stawka za okres 57 n - liczba okresów 58 ---------------------- ---------------------------------------------- 59 sub Finance FutureValue 60 mój pv, r, n 61 mój fv pv 1 r 100 n 62 powrót fv 63 64 65 ----------------------------- --------------------------------------- 66 Wartość bieżąca inwestycji przyznanej 67 fv - a wartość przyszła 68 r - stawka za okres 69 n - liczba okresów 70 ----------------------------------- ---------------------------------- 71 sub Finance PresentValue 72 mój pv 73 mój fv, r, n 74 pv fv 1 r 100 n 75 return pv 76 77 78 79 --------------------------------------- ------------------------------------- 80 Otrzymaj przyszłą wartość renty z tytułu 81 mp - miesięczna płatność renty 82 r - stawka za okres 8 3 n - liczba okresów 84 ------------------------------------------- ------------------------- 85 86 sub FVofAnnuity 87 mój fv 88 mój jedenR 89 mój mp, r, n 90 91 jedenR 1 rn 92 fv mp 1R - 1 r 93 powrót fv 94 95 96 ---------------------------------------- Otrzymuj rentę z następujących informacji 98 r - stawka za okres 99 n - liczba okresów 100 fv - wartość przyszła 101 -------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 1 rn 109 mp fv r 1 R - 1 110 mp powrót 111 112 113 ----------------------------------- -------------------------------- 114 Zdobądź średnią z ostatnich n wartości w tablicy 115 ---- -------------------------------------------------- -------------- 116 Ostatni licznik liczba elementów z tablicy w wartościach 117 Całkowita liczba elementów w wartościach jest w liczbie 118 119 sub getLastAverage 120 mój licznik, n umber, wartości 121 mój i 122 123 mój 0 124 zwrot 0 jeśli liczba 0 125 dla i 0 i liczba i 126 a liczba wartości - i - 1 127 128 zwraca liczbę 129 130 131 ---------- -------------------------------------------------- -------- 132 Pobierz średnią ruchome wartości 133 --------------------------------- ---------------------------------- 134 Rozmiar okna to pierwszy parametr, liczba elementów w 135 jest następna Jest to łatwe do wyliczenia w ramach funkcji 136 za pomocą funkcji skalarnej, ale przedstawiona poniżej procedura 137 jest również wykorzystywana do zilustrowania sposobu przekazywania wskaźników Odniesienie do tablicy wartości 138 jest przekazywane dalej, a następnie odniesienie do miejsca 139 wartość zwracana ma być zapisana 140 141 sub getMovingAve 142 mój licznik, liczba, wartości, ruchAve 143 moje i 144 moje 0 145 moje v 0 146 147 zwraca 0 jeśli liczba 0 148 zwraca -1 jeśli liczba 149 return -2 jeśli liczba 2 150 151 movingAve 0 0 152 movingAve number - 1 0 153 dla i 0 i count i 154 v wartości i 1 55 z pozycji 156 ruchAve i 0 157 158 dla i liczenie i liczba i 159 wartości v i 160 lic. 161 v wartości i - licznik - 1 162 a - v count 163 movingAve ia 164 165 return 0 166 167 168 1.Zauważyć na deklaracja funkcji FutureValue ze znakami trzech dolców oznaczają trzy liczby skalarne przekazywane do funkcji Ta dodatkowa scoping jest używana do sprawdzania typu parametrów przekazanych do funkcji Jeśli miałbyś przekazać ciąg zamiast liczby do funkcji , otrzymasz wiadomość bardzo podobną do tej. Zbyt wiele argumentów dla Finance FutureValue w wierszu 15, w pobliżu czasu Wykonanie przerwania z powodu błędów kompilacji. Użycie prototypów podczas definiowania funkcji uniemożliwia wysyłanie wartości innych niż oczekiwana funkcja Użyj lub, aby przejść w tablicy wartości Jeśli przechodzisz przez odniesienie, użyj lub, aby wyświetlić skalarne odniesienie do tablicy lub hash Jeśli nie używasz odwrotnego ukośnika, wszystkie inne typy w prototypie listy argumentów ignorowane Inne typy dyskwalifikatorów zawierają znak ampersand i odniesienie do funkcji, gwiazdkę dowolnego typu i średnik, aby wskazywać, że wszystkie inne parametry są opcjonalne. Teraz spójrz na deklarację funkcji lastMovingAverage, która określa dwie liczby całkowite w przód a następnie tablica Sposób użycia argumentów w funkcji polega na przypisaniu wartości do każdego z dwóch skalarów, liczby i liczby, podczas gdy wszystko inne jest wysyłane do tablicy Spójrz na funkcję getMovingAverage, aby zobaczyć, jak dwie macierze są przekazywane aby uzyskać średnią ruchomą na liście wartości. Sposób na wywołanie funkcji getMovingAverage pokazano na listingu 4 5 Listing 4 5 Korzystanie z funkcji średniej ruchomej 1 usr bin perl - w 2 3 push Inc, pwd 4 use Finanse 5 6 Wartości 12,22,23,24,21,23,24,23,23,21,29,27,26,28 7 mv 0 8 wielkościowe wartości skalarne 9 print n wartości do pracy z n 10 print liczba wartości wielkości n 11 12 ------------------------------------------------ ---------------- 13 Oblicz a verage powyższej funkcji 14 -------------------------------------------- -------------------- 15 ave Finanse getLastAverage 5, rozmiar, wartości 16 print n Średnia z ostatnich 5 dni ave n 17 18 Finanse getMovingAve 5, rozmiar, wartości, mv 19 print n Średnia ruchoma z 5-dniowym okienkiem n n. Oto dane wyjściowe z listy 4 5 Wartości do pracy z liczbą wartości 14. Średnie zużycie ostatnich 5 dni 26 2. Funkcja getMovingAverage przyjmuje dwa skale, a następnie dwa odniesienia do tablic jako skalary W obrębie funkcji, dwa skalarne tablice są dereferowane do użycia jako tablice numeryczne Zwracany zestaw wartości jest wstawiany w obszarze przekazanym jako drugie odniesienie Jeśli parametry wejściowe nie zostały określone dla każdej odnośnej tablicy, tablica movingAve odniesienie byłoby puste i spowodowałoby błędy w czasie wykonywania Innymi słowy, poniższe oświadczenie nie jest poprawne sub getMovingAve. W wyniku spew komunikatów o błędach z nieprawidłowego prototypu funkcji jest następujący Użycie niezainicjalizowanego wartość przy linii 128 Użycie niezainicjowanej wartości w wierszu 128 Użycie niezainicjowanej wartości w wierszu 128 Użycie niezainicjowanej wartości w wierszu 128 Użycie niezainicjowanej wartości w wierszu 128 Użycie niezainicjowanej wartości w wierszu 128 Użycie niezainicjowanej wartości w wierszu 133 Użycie niezainicjowanej wartości w wierszu 135 Użycie niezainicjowanej wartości przy linii 133 Wykorzystanie wartości niezainicjowanej na linii 135 Wykorzystanie wartości niezainicjowanej na linii 133 Wykorzystanie wartości niezainicjowanej na linii 133 Wykorzystanie niezainicjowanej wartości w linii 133 Wykorzystanie wartości niezainicjowanej na linii 135 Wykorzystanie niezainicjowanej wartości w wierszu 133 Wykorzystanie niezainicjowanej wartości na wiersz 135 Wykorzystanie wartości niezainicjowanej na linii 133 Wykorzystanie wartości niezainicjowanej na linii 135 Wykorzystanie wartości niezainicjowanej na linii 135 Wykorzystanie wartości niezainicjowanej na linii 135 Wykorzystanie wartości niezainicjowanej na linii 133 Wykorzystanie wartości niezainicjowanej na linii 135 Wykorzystanie niezainicjowanej wartości w linii 133 Wykorzystanie wartości niezainicjowanej w wierszu 135.Average z ostatnich 5 dni 26 2.Moving Average z 5-dniowym okienkiem. Jest to oczywiście nie poprawne wyjście Dlatego też jest to crit ical, które przechodzą przez odniesienie przy wysyłaniu więcej niż jednej tablicy. Zaznaczenia globalne do wykorzystania w pakiecie można również zadeklarować Spójrz na poniższy fragment kodu z modułu, aby zobaczyć, jaka wartość domyślna zmiennej Interest będzie, jeśli nic nie zostało podane na wejściu Aktualny moduł wymaga odsetek, które mają zostać przekazane, ale można to zmienić. Tutaj trochę fragment kodu, który może zostać dodany do końca programu pokazanego na listingu 4 5, aby dodać możliwość ustawiania stóp procentowych 20 local defaultInterest 5 0 21 sub Finance SetInterest 22 moja zmiana stawki 23 rate -1 jeśli rate 0 24 defaultInterest rate 25 printf n defaultInterest rate 26. Zadeklarowana jest lokalna zmienna domyślnaInterest w wierszu 20 Podprogram SetInterest do modyfikowania szybkości jest zadeklarowany w wierszach 21 through 26 The rate variable uses the values passed into the subroutine and simply assigns a positive value for it You can always add more error checking if necessary. To access the defaultInterest variable s value, you could define either a subroutine that returns the value or refer to the value directly with a call to the following in your application program Finance defaultInterest. The variable holding the return value from the module function is declared as my variable The scope of this variable is within the curly braces of the function only When the called subroutine returns, the reference to my variable is returned If the calling program uses this returned reference somewhere, the link counter on the variable is not zero therefore, the storage area containing the returned values is not freed to the memory pool Thus, the function that declares my pv. and then later returns the value of pv returns a reference to the value stored at that location If the calling routine performs a call like this one Finance FVofAnnuity monthly, rate, time. there is no variable specified here into which Perl stores the returned reference therefore, any returned value or a list of values is destroyed Instead , the call with the returned value assigned to a local variable, such as this one fv Finance FVofAnnuity monthly, rate, time. maintains the variable with the value Consider the example shown in Listing 4 6, which manipulates values returned by functions Listing 4 6 Sample usage of the my function 1 usr bin perl - w 2 3 push Inc, pwd 4 use Finance 5 6 monthly 400 7 rate 0 2 i e 6 APR 8 time 36 in months 9 10 print n ------------------------------------------------ 11 fv Finance FVofAnnuity monthly, rate, time 12 printf n For a monthly 8 2f at a rate of 6 2f for d periods , 13 monthly, rate, time 14 printf n you get a future value of 8 2f , fv 15 16 fv 1 1 allow 10 gain in the house value 17 18 mo Finance AnnuityOfFV fv, rate, time 19 20 printf n To get 10 percent more at the end, i e 8 2f , fv 21 printf n you need a monthly payment value of 8 2f , mo, fv 22 23 print n ------------------------------------------------ n. Here is sample input and output for this function testme ---------- -------------------------------------- For a monthly 400 00 at a rate of 0 20 for 36 periods you get a future value of 1415603 75 To get 10 percent more at the end, i e 1557164 12 you need a monthly payment value of 440 00.Modules implement classes in a Perl program that uses the object-oriented features of Perl Included in object-oriented features is the concept of inheritance You ll learn more on the object-oriented features of Perl in Chapter 5 Object-Oriented Programming in Perl Inheritance means the process with which a module inherits the functions from its base classes A module that is nested within another module inherits its parent modules functions So inheritance in Perl is accomplished with the construct Here s the basic syntax SuperClass NextSubClass ThisClass. The file for these is stored in SuperClass NextSubClass Each double colon indicates a lower-level directory in which to look for the module Each module, in turn, declares itself as a package with statements like the f ollowing package SuperClass NextSubClass package SuperClass NextSubClass EvenLower. For example, say that you really want to create a Money class with two subclasses, Stocks and Finance Here s how to structure the hierarchy, assuming you are in the usr lib perl5 directory. Create a Money directory under the usr lib perl5 directory. Copy the existing file into the Money subdirectory. Create the new file in the Money subdirectory. Edit the file to use the line package Money Finance instead of package Finance. Edit scripts to use Money Finance as the subroutine prefix instead of Finance. Create a file in the usr lib perl5 directory. The Perl script that gets the moving average for a series of numbers is presented in Listing 4 7 Listing 4 7 Using inheriting modules 1 usr bin perl - w 2 aa pwd 3 aa Money 4 push Inc, aa 5 use Money Finance 6 values 12,22,23,24,21,23,24,23,23,21,29,27,26,28 7 mv 0 8 size scalar values 9 print n Values to work with n 10 print Number of values size n 11 -------- -------------------------------------------------------- 12 Calculate the average of the above function 13 ---------------------------------------------------------------- 14 ave Money Finance getLastAverage 5, size, values 15 print n Average of last 5 days ave n 16 Money Finance getMovingAve 5, size, values, mv 17 foreach i values 18 print n Moving with 5 days window mv i n 19 20 print n Moving Average with 5 days window n n. Lines 2 through 4 add the path to the Money subdirectory The use statement in line 5 now addresses the file in the Money subdirectory The calls to the functions within are now called with the prefix Money Finance instead of Finance Therefore, a new subdirectory is shown via the symbol when Perl is searching for modules to load. The file is not required Even so, you should create a template for future use Actually, the file would be required to put any special requirements for initialization that the entire hierarchy of modules uses The code for initialization i s placed in the BEGIN function The sample file is shown in Listing 4 8 Listing 4 8 The superclass module for 1 package Money 2 require Exporter 3 4 BEGIN 5 printf n Hello Zipping into existence for you n 6 7 1.To see the line of output from the printf statement in line 5, you have to insert the following commands at the beginning of your Perl script use Money use Money Finance. To use the functions in the module, you use this line use Money Stocks. The file appears in the Money subdirectory and is defined in the same format as the file, with the exceptions that use Stocks is used instead of use Finance and the set of functions to export is different. A number of modules are included in the Perl distribution Check the usr lib perl5 lib directory for a complete listing after you install Perl There are two kinds of modules you should know about and look for in your Perl 5 release, Pragmatic and Standard modules. Pragmatic modules, which are also like pragmas in C compiler directives, tend to affect the compilation of your program They are similar in operation to the preprocessor elements of a C program Pragmas are locally scoped so that they can be turned off with the no command Thus, the command no POSIX. turns off the POSIX features in the script These features can be turned back on with the use statement. Standard modules bundled with the Perl package include several functioning packages of code for you to use Refer to appendix B, Perl Module Archives, for a complete list of these standard modules. To find out all the modules installed on your system, issue the following command If you get an error, add the usr lib perl5 directory to your path find usr lib perl5 - name perl - print. Extension modules are written in C or a mixture of Perl and C and are dynamically loaded into Perl if and when you need them These types of modules for dynamic loading require support in the kernel Solaris lets you use these modules For a Linux machine, check the installation pages on how to upgr ade to the ELF format binaries for your Linux kernel. The term CPAN Comprehensive Perl Archive Network refers to all the hosts containing copies of sets of data, documents, and Perl modules on the Net To find out about the CPAN site nearest you, search on the keyword CPAN in search engines such as Yahoo AltaVista, or Magellan A good place to start is the site. This chapter introduced you to Perl 5 modules and described what they have to offer A more comprehensive list is found on the Internet via the addresses shown in the Web sites and. A Perl package is a set of Perl code that looks like a library file A Perl module is a package that is defined in a library file of the same name A module is designed to be reusable You can do some type checking with Perl function prototypes to see whether parameters are being passed correctly A module has to export its functions with the EXPORT array and therefore requires the Exporter module Modules are searched for in the directories listed in the Inc array. Obviously, there is a lot more to writing modules for Perl than what is shown in this chapter The simple examples in this chapter show you how to get started with Perl modules In the rest of the book I cover the modules and their features, so hang in there. I cover Perl objects, classes, and related concepts in Chapter 5.CPAN is in the industry standards, technical specifications and software library and framework skills category The next table is for comparison with the above and provides summary statistics for all permanent job vacancies with a requirement for industry standards, technical specifications and software library and framework skills. Software Library Framework Skills. Permanent job vacancies with a requirement for industry standards, technical specifications and software library and framework skills. As of all permanent IT job vacancies advertised in the UK. Number of salaries quoted. Median salary change year-on-year.90 offered a salary of more than.10 offered a salary of more than. UK excluding London median salary. CPAN Job Vacancy Trend. The job posting trend of jobs advertised citing CPAN as a proportion of all permanent or contract IT jobs with a match in the Libraries, Frameworks Software Standards category. CPAN Salary Trend. This chart provides the 3-month moving average for salaries quoted in permanent IT jobs citing CPAN. CPAN Salary Histogram. The salary distribution of IT jobs citing CPAN over the 3 months to 14 March 2017.CPAN Top 4 Job Locations. The table below looks at the demand and provides a guide to the median salaries quoted in IT jobs citing CPAN within the UK over the 3 months to 14 March 2017 The Rank Change column provides an indication of the change in demand within each location based on the same 3 month period last year. Rank Change on Same Period Last Year. Matching Permanent IT Job Ads. Median Salary Last 3 Months. The belief that a change will be easy to do correctly makes it less likely that the change will be done correctly. An XP programmer writes a unit test to clarify his intentions before he makes a change We call this test-driven design TDD or test-first programming because an API s design and implementation are guided by its test cases The programmer writes the test the way he wants the API to work, and he implements the API to fulfill the expectations set out by the test. Test-driven design helps us invent testable and usable interfaces In many ways, testability and usability are one in the same If you can t write a test for an API, it ll probably be difficult to use, and vice-versa Test-driven design gives feedback on usability before time is wasted on the implementation of an awkward API As a bonus, the test documents how the API works, by example. All of the above are good things, and few would argue with them One obvious concern is that test-driven design might slow down development It does take time to write tests, but by writing the tests first, you gain insight into the implementation, which speeds development Debugging the implementation is faster, too, thanks to immediate and reproducible feedback that only an automated test can provide. Perhaps the greatest time savings from unit testing comes a few months or years after you write the test, when you need to extend the API The unit test not only provides you with reliable documentation for how the API works, but it also validates the assumptions that went into the design of the API You can be fairly sure a change didn t break anything if the change passes all the unit tests written before it Changes that fiddle with fundamental API assumptions cause the costliest defects to debug A comprehensive unit test suite is probably the most effective defense against such unwanted changes. This chapter introduces test-driven design through the implementation of an exponential moving average EMA , a simple but useful mathematical function This chapter also explains how to use the CPAN modules Test More and Test Exception. A unit test validat es the programmer s view of the application This is quite different from an acceptance test, which is written from the customer s perspective and tests end-user functionality, usually through the same interface that an ordinary user uses In constrast, a unit test exercises an API, formally known as a unit Usually, we test an entire Perl package with a single unit test. Perl has a strong tradition of unit testing, and virtually every CPAN module comes with one or more unit tests There are also many test frameworks available from CPAN This and subsequent chapters use Test More a popular and well documented test module 2 I also use Test Exception to test deviance cases that result in calls to die 3.Test First, By Intention. Test-driven design takes unit testing to the extreme Before you write the code, you write a unit test For example, here s the first test case for the EMA exponential moving average module. This is the minimal Test More test You tell Test More how many tests to expect, and you import the module with useok as the first test case The BEGIN ensures the module s prototypes and functions are available during compilation of the rest of the unit test. The next step is to run this test to make sure that it fails. At this stage, you might be thinking, Duh Of course, it fails Test-driven design does involve lots of duhs in the beginning The baby steps are important, because they help to put you in the mindset of writing a small test followed by just enough code to satisfy the test. If you have maintenance programming experience, you may already be familiar with this procedure Maintenance programmers know they need a test to be sure that their change fixes what they think is broken They write the test and run it before fixing anything to make sure they understand a failure and that their fix works Test-driven design takes this practice to the extreme by clarifying your understanding of all changes before you make them. Now that we have clarified the need for a module called EMA duh , we implement it. And, duh, the test passes. Yeeha Time to celebrate with a double cappuccino so we don t fall asleep. That s all there is to the test-driven design loop write a test, see it fail, satisfy the test, and watch it pass For brevity, the rest of the examples leave out the test execution steps and the concomitant duhs and yeehas However, it s important to remember to include these simple steps when test-first programming If you don t remember, your programming partner probably will 4.Exponential Moving Average. Our hypothetical customer for this example would like to maintain a running average of closing stock prices for her website An EMA is commonly used for this purpose, because it is an efficient way to compute a running average You can see why if you look at the basic computation for an EMA. today s price x weight yesterday s average x 1 - weight. This algorithm produces a weighted average that favors recent history The effect of a price on the average decays exponentially over time It s a simple function that only needs to maintain two values yesterday s average and the weight Most other types of moving averages, require more data storage and more complex computations. The weight, commonly called alpha is computed in terms of uniform time periods days, in this example.2 number of days 1.For efficiency, alpha is usually computed once, and stored along with the current value of the average I chose to use an object to hold these data and a single method to compute the average. Test Things That Might Break. Since the first cut design calls for a stateful object, we need to instantiate it to use it The next case tests object creation. I sometimes forget to return the instance self so the test calls ok to check that new returns some non-zero value This case tests what I think might break An alternative, more extensive test is. This case checks that new returns a blessed reference of class EMA To me, this test is unnecessarily complex If new returns s omething, it s probably an instance It s reasonable to rely on the simpler case on that basis alone Additionally, there will be other test cases that will use the instance, and those tests will fail if new doesn t return an instance of class EMA. This point is subtle but important, because the size of a unit test suite matters The larger and slower the suite, the less useful it will be A slow unit test suite means programmers will hesitate before running all the tests, and there will be more checkins which break unit and or acceptance tests Remember, programmers are lazy and impatient, and they don t like being held back by their programming environment When you test only what might break, your unit test suite will remain a lightweight and effective development tool. Please note that if you and your partner are new to test-driven design, it s probably better to err on the side of caution and to test too much With experience, you ll learn which tests are redundant and which are especially helpful There are no magic formulas here Testing is an art that takes time to master. Satisfy The Test, Don t Trick It. Returning to our example, the implementation of new that satisfies this case is. This is the minimal code which satisfies the above test length doesn t need to be stored, and we don t need to compute alpha We ll get to them when we need to. But wait, you say, wouldn t the following code satisfy the test, too. Yes, you can trick any test However, it s nice to treat programmers like grown-ups even though we don t always act that way No one is going to watch over your shoulder to make sure you aren t cheating your own test The first implementation of new is the right amount of code, and the test is sufficient to help guide that implementation The design calls for an object to hold state, and an object creation is what needed to be coded. Test Base Cases First. What we ve tested thus far are the base cases that is, tests that validate the basic assumptions of the API When we te st basic assumptions first, we work our way towards the full complexity of the complete implementation, and it also makes the test more readable Test-first design works best when the implementation grows along with the test cases. There are two base cases for the compute function The first base case is that the initial value of the average is just the number itself There s also the case of inputting a value equal to the average, which should leave the average unchanged These cases are coded as follows. The is function from Test More lets us compare scalar values Note the change to the instantiation test case that allows us to use the instance ema for subsequent cases Reusing results of previous tests shortens the test, and makes it easier to understand. The implementation that satisfies these cases is. The initialization of alpha was added to new because compute needs the value new initializes the state of the object, and compute implements the EMA algorithm self - is initially undef so tha t case can be detected. Even though the implementation looks finished, we aren t done testing The above code might be defective Both compute test cases use the same value, and the test would pass even if, for example, self - and value were accidentally switched We also need to test that the average changes when given different values The test as it stands is too static, and it doesn t serve as a good example of how an EMA works. Choose Self-Evident Data. In a test-driven environment, programmers use the tests to learn how the API works You may hear that XPers don t like documentation That s not quite true What we prefer is self-validating documentation in the form of tests We take care to write tests that are readable and demonstrate how to use the API. One way to create readable tests is to pick good test data However, we have a little bootstrapping problem To pick good test data, we need valid values from the results of an EMA computation, but we need an EMA implementation to give us thos e values One solution is to calculate the EMA values by hand Or, we could use another EMA implementation to come up with the values While either of these choices would work, a programmer reading the test cases would have to trust them or to recompute them to verify they are correct Not to mention that we d have to get the precision exactly right for our target platform. Use The Algorithm, Luke. A better alternative is to work backwards through the algorithm to figure out some self-evident test data 5 To accomplish this, we treat the EMA algorithm as two equations by fixing some values Our goal is to have integer values for the results so we avoid floating point precision issues In addition, integer values make it easier for the programmer to follow what is going on. When we look at the equations, we see alpha is the most constrained value. today s average today s price x alpha yesterday s average x 1 - alpha. alpha 2 length 1.Therefore it makes sense to try and figure out a value of alpha t hat can produce integer results given integer prices. Starting with length 1, the values of alpha decrease as follows 1, 2 3, 1 2, 2 5, 1 3, 2 7, and 1 4 The values 1, 1 2, and 2 5 are good candidates, because they can be represented exactly in binary floating point 1 is a degenerate case, the average of a single value is always itself 1 2 is not ideal, because alpha and 1 - alpha are identical, which creates a symmetry in the first equation. today s average today s price x 0 5 yesterday s average x 0 5.We want asymmetric weights so that defects, such as swapping today s price and yesterday s average, will be detected A length of 4 yields an alpha of 2 5 0 4 , and makes the equation asymmetric. today s average today s price x 0 4 yesterday s average x 0 6.With alpha fixed at 0 4, we can pick prices that make today s average an integer Specifically, multiples of 5 work nicely I like prices to go up, so I chose 10 for today s price and 5 for yesterday s average the initial price This makes today s average equal to 7, and our test becomes. Again, I revised the base cases to keep the test short Any value in the base cases will work so we might as well save testing time through reuse. Our test and implementation are essentially complete All paths through the code are tested, and EMA could be used in production if it is used properly That is, EMA is complete if all we care about is conformant behavior The implementation currently ignores what happens when new is given an invalid value for length. Although EMA is a small part of the application, it can have a great impact on quality For example, if new is passed a length of -1, Perl throws a divide-by-zero exception when alpha is computed For other invalid values for length such as -2, new silently accepts the errant value, and compute faithfully produces non-sensical values negative averages for positive prices We can t simply ignore these cases We need to make a decision about what to do when length is invalid. One approach wou ld be to assume garbage-in garbage-out If a caller supplies -2 for length it s the caller s problem Yet this isn t what Perl s divide function does, and it isn t what happens, say, when you try to de-reference a scalar which is not a reference The Perl interpreter calls die and I ve already mentioned in the Coding Style chapter that I prefer failing fast rather than waiting until the program can do some real damage In our example, the customer s web site would display an invalid moving average, and one her customers might make an incorrect investment decision based on this information That would be bad It is better for the web site to return a server error page than to display misleading and incorrect information. Nobody likes program crashes or server errors Yet calling die is an efficient way to communicate semantic limits couplings within the application The UI programmer, in our example, may not know that an EMA s length must be a positive integer He ll find out when the application dies He can then change the design of his code and the EMA class to make this limit visible to the end user Fail fast is an important feedback mechanism If we encounter an unexpected die it tells us the application design needs to be improved. Deviance Testing. In order to test for an API that fails fast, we need to be able to catch calls to die and then call ok to validate the call did indeed end in an exception The function diesok in the module Test Exception does this for us. Since this is our last group of test cases in this chapter, here s the entire unit test with the changeds for the new deviance cases highlighted. There are now 9 cases in the unit test The first deviance case validates that length can t be negative We already know -1 will die with a divide-by-zero exception so -2 is a better choice The zero case checks the boundary condition The first valid length is 1 Lengths must be integers, and 2 5 or any other floating point number is not allowed length has no explicit upper limit Perl automatically converts integers to floating point numbers if they are too large The test already checks that floating point numbers are not allowed so no explicit upper limit check is required. The implementation that satisfies this test follows. The only change is the addition of a call to die with an unless clause This simple fail fast clause doesn t complicate the code or slow down the API, and yet it prevents subtle errors by converting an assumption into an assertion. Only Test The New API. One of the most difficult parts of testing is to know when to stop Once you have been test-infected, you may want to keep on adding cases to be sure that the API is perfect For example, a interesting test case would be to pass a NaN Not a Number to compute but that s not a test of EMA The floating point implementation of Perl behaves in a particular way with respect to NaNs 6 and Bivio Math EMA will conform to that behavior Testing that NaNs are handled properly is a job for the Perl int erpreter s test suite. Every API relies on a tremendous amount of existing code There isn t enough time to test all the existing APIs and your new API as well Just as an API should separate concerns so must a test When testing a new API, your concern should be that API and no others. Solid Foundation. In XP, we do the simplest thing that could possibly work so we can deliver business value as quickly as possible Even as we write the test and implementation, we re sure the code will change When we encounter a new customer requirement, we refactor the code, if need be, to facilitate the additional function This iterative process is called continuous design which is the subject of the next chapter It s like renovating your house whenever your needs change 7.A system or house needs a solid foundation in order to support continuous renovation Unit tests are the foundation of an XP project When designing continuously, we make sure the house doesn t fall down by running unit tests to validate al l the assumptions about an implementation We also grow the foundation before adding new functions Our test suite gives us the confidence to embrace change. Quality Software Management Vol 1 Systems Thinking Gerald Weinberg, Dorset House, 1991, p 236.Part of the Test-Simple distribution, available at I used version 0 47 for this book. Just a friendly reminder to program in pairs, especially when trying something new. Thanks to Ion Yadigaroglu for teaching me this technique. In some implementations, use of NaNs will cause a run-time error In others, they will cause all subsequent results to be a NaN. Don t let the thought of continuous house renovation scare you off Programmers are much quieter and less messy than construction workers.
Comments
Post a Comment