-
X-Received: by 10.140.36.231 with SMTP id p94mr58989qgp.13.1415745443135; Tue, 11 Nov
2014 14:37:23 -0800 (PST)
X-Received: by 10.140.36.231 with SMTP id p94mr58989qgp.13.1415745443135; Tue, 11 Nov
2014 14:37:23 -0800 (PST)
Path: news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed2.atman.pl!newsfeed.atman.pl!go
blin2!goblin.stu.neva.ru!newsfeed.xs4all.nl!newsfeed4.news.xs4all.nl!xs4all!new
speer1.nac.net!border2.nntp.dca1.giganews.com!border1.nntp.dca1.giganews.com!nn
tp.giganews.com!i13no947605qae.0!news-out.google.com!u1ni6qah.0!nntp.google.com
!i13no947604qae.0!postnews.google.com!glegroupsg2000goo.googlegroups.com!not-fo
r-mail
Newsgroups: pl.comp.programming
Date: Tue, 11 Nov 2014 14:37:23 -0800 (PST)
In-Reply-To: <5...@g...com>
Complaints-To: g...@g...com
Injection-Info: glegroupsg2000goo.googlegroups.com; posting-host=89.67.197.135;
posting-account=f7iIKQoAAAAkDKpUafc-4IXhmRAzdB5r
NNTP-Posting-Host: 89.67.197.135
References: <5...@g...com>
User-Agent: G2/1.0
MIME-Version: 1.0
Message-ID: <8...@g...com>
Subject: Re: Makra higieniczne w jezyku Scheme
From: g...@g...com
Injection-Date: Tue, 11 Nov 2014 22:37:23 +0000
Content-Type: text/plain; charset=ISO-8859-1
Lines: 229
Xref: news-archive.icm.edu.pl pl.comp.programming:206922
[ ukryj nagłówki ]* MAKRA OPARTE NA PRZEKSZTALCANIU WZORCOW (syntax-rules)
** PROBLEMY Z "define-macro"
W poprzednim rozdziale udalo sie uzyskac dosc zgrabna metodologie pisania
makr, ktore sa stosunkowo proste w analizie. Pewien problem dotyczyl
destrukturyzacji danych wejsciowych do makra -- uzywalismy do tego
funkcji zbudowanych w oparciu o funkcje car i cdr.
Innym problemem bylo to, ze moglismy niechcacy stworzyc makro, ktore
wprowadza jakas zmienna kolidujaca z istniejaca zmienna. Z tym problemem
jakos sobie poradzilismy, uzywajac funkcji gensym. Nalezy jednak zauwazyc,
ze nie rozwiazuje to wszystkich mozliwych problemow. Niekiedy makro moze
odnosic sie do jakichs zewnetrznych wiazan, ktore moglyby niechcacy
zostac przesloniete przez uzytkownika makra w kontekscie jego uzycia.
Na przyklad wartosc wyrazenia
: (let ((if list))
: (cond ((= 2 3) 'a)
: (else 'b)))
przy podanej wczesniej definicji makra "cond" moze okazac sie zaskakujaca:
nie bedzie to bowiem symbol a, tak jak moglibysmy sie spodziewac, tylko lista
(#f a (#t b)).
Moglibysmy zatem oczekiwac od naszego systemu makr, zeby zachowywaly
"przezroczystosc odniesieniowa", tzn. zeby uzywane w nich identyfikatory
odnosily sie do wartosci z kontekstu definicji makra, a nie z do wartosci
z kontekstu jego uzycia (chyba ze zazadamy tego explicite). Tylko bowiem
w taki sposob mozemy uwolnic uzytkownika makr od koniecznosci martwienia
sie jego szczegolami implementacyjnymi.
Kolejnym problemem, o ktorym nie wspominalismy, byl brak mozliwosci
tworzenia makr o ograniczonym zasiegu -- forma define-macro wprowadzala
nowe makro w biezacym zasiegu. Dla odmiany, mozemy z latwoscia definiowac
sobie pomocnicze funkcje wewnatrz innych funkcji. (Wprawdzie mozna
sobie zdefiniowac makro pomocnicze wewnatrz funkcji, jednak nie mozna
uzyc makra o ograniczonym zasiegu do utworzenia definicji w zasiegu
globalnym.)
** ROZWIAZANIE: MAKRA HIGIENICZNE "syntax-rules"
Standard R5RS jezyka Scheme definiuje trzy nowe podstawowe formy specjalne
sluzace do definiowania makr, mianowicie define-syntax, let-syntax
i letrec-syntax. W przeciwienstwie do define-macro, owe formy jako swoje
argumenty przyjmuja procedury (np. stworzone przy pomocy wyrazen lambda).
Specyfika tych procedur jest dosc skomplikowana i przyjrzymy sie jej blizej
dopiero w nastepnym rozdziale.
Tymczasem omowie pewna wtorna forme specjalna "syntax-rules", ktora
ulatwia tworzenie odpowiednich procedur. Postac owej formy jest nastepujaca:
: (syntax-rules (identyfikatory-specjalne ...)
: ((wzorzec przeksztalcenie)
: ...))
Formy "wzorzec" i "przeksztalcenie" wyrazone sa w specjalnym jezyku
wzorcow. Nie bedziemy zglebiac tego, w jaki sposob ow jezyk jest
implementowany. Osoby zainteresowane moga zajrzec do implementacji
stworzonej przez Abdulaziza Ghulouma i R. Kenta Dybviga w jezyku Scheme:
https://www.cs.indiana.edu/l/www/chezscheme/r6rs-lib
raries/
Forma "wzorzec" jest odpowiedzialna za destrukturyzacje i opisuje,
jakie jest zamierzone uzycie makra, natomiast forma "przeksztalcenie"
jest odpowiedzialna za restrukturyzacje formy w terminach bardziej
pierwotnych. Formy skladaja sie z dowolnie zagniezdzonych list
symboli oraz ewentualnie specjalnego symbolu "..." (elipsy).
Jezeli dane uzycie makra nie pasuje do danego wzorca, to procedura
wygenerowana przez "syntax-rules" probuje je dopasowac do kolejnego
wzorca, a jezeli zaden wzorzec nie pasuje do danego uzycia, procedura
zglasza blad skladni.
** NOWA DEFINICJA FORMY "let"
Zamiast szczegolowo wyjasniac jezyk wzorcow makra "syntax-rules",
najlepiej przedstawic kilka praktycznych przykladow. Zaczniemy
od redefinicji omowionego wczesniej makra "let". Nowa definicja,
wyrazona w termiach jezyka wzorcow, bedzie miala postac:
: (define-syntax let
: (syntax-rules ()
: ((let ((name value) ...) body + ...)
: ((lambda (name ...) body + ...) value ...))))
Warto zwrocic uwage na kilka rzeczy. Po pierwsze, nasza definicja
wyglada prawie dokladnie tak, jak wczesniejsza specyfikacja makra "let".
Glowna roznica jest taka, ze musimy owinac te specyfikacje w czarodziejskie
zaklecie. Poza tym w specyfikacji nasz wielokropek traktowalismy nieformalnie,
natomiast tutaj jest on uzyty jako literalny symbol o dobrze okreslonej
semantyce -- musi on wystapic za jakims identyfikatorem (albo dowolnie
zagniezdzona lista zawierajaca przynajmniej jeden zwykly identyfikator)
i oznacza zero lub wiecej wystapien danego elementu.
Ponadto, jezeli w formie "wzorzec" wielokropek wystepuje za jakims
identyfikatorem (byc moze zagniezdzonym w liscie), to ilekroc uzyjemy
tego identyfikatora w formie "przeksztalcenie", musimy za nim rowniez
dostawic wielokropek.
Kolejna uwaga dotyczy tego, ze w naszej definicji nazwa makra ("let")
pojawia sie dwa razy: jako pierwszy argument formy "define-syntax" oraz
jako pierwszy element formy "wzorzec".
Tak naprawde tylko pierwsze z tych uzyc ma realne znaczenie -- pierwszy
element formy "wzorzec" i tak jest ignorowany przez transformator, wiec
mozna by bylo w jego miejsce wstawic dowolny symbol. Uzycie jakiegokolwiek
symbolu innego niz nazwa makra mogloby byc mylace, ale powszechnie
stosowanym idiomem jest uzywanie w tej pozycji symbolu "_":
: (define-syntax let
: (syntax-rules ()
: ((_ ((name value) ...) body + ...)
: ((lambda (name ...) body + ...) value ...))))
Mozna tez zauwazyc, ze nasze makro jest stosunkowo proste -- wystepuje
w nim tylko jedna para (wzorzec przeksztalcenie), zas lista
identyfikatory-specjalne jest pusta.
Do definiowania takich przypadkow moglibysmy zdefiniowac makro pomocnicze:
: (define-syntax define-syntax-rule
: ((_ (name . args) transfomation)
: (define-syntax name
: (syntax-rules ()
: ((name . args)
: transformation)))))
Dzieki niemu nasza definicja przyjmie jeszcze prostsza postac:
: (define-syntax-rule (let ((name value) ...) body + ...)
: ((lambda (name ...) body + ...) value ...))
** NOWE DEFINICJE SPOJNIKOW LOGICZNYCH "and" i "or"
Definicja formy "and" bedzie raczej prosta:
: (define-syntax and
: (syntax-rules ()
: ((_ p)
: p)
: ((_ p q ...)
: (if p (and q ...) #f))))
Glowna roznica wzgledem poprzedniego makra jest taka, ze tym razem
w formie "syntax-rules" pojawiaja sie dwa przypadki. Odpowiada to
dwom przypadkom z formy "if" we wczesniejszym wariancie wyrazonym
przy pomocy define-macro.
Definicja formy "or" rowniez nie powinna nastreczac wiekszych trudnosci:
: (define-syntax or
: (syntax-rules ()
: ((_ p)
: p)
: ((_ p q ...)
: (let ((T p)) (if T T (or q ...))))))
Skorzystalismy tutaj z faktu, ze makra zachowuja higiene, i transformator
makr sam zadba o to, zeby uzyta przez nas nazwa zmiennej tymczasowej
nie kolidowala z zadna nazwa wystepujaca w kontekscie uzycia makra.
** NOWA DEFINICJA FORMY "cond"
: (define-syntax cond
: (syntax-rules (else)
: ((_ (else actions + ...))
: (begin actions + ...))
:
: ((_ (condition actions + ...))
: (if condition
: (begin actions + ...)))
:
: ((_ (condition actions + ...) other-clauses ...)
: (if condition
: (begin actions + ...)
: (cond other-clauses ...)))))
Powyzsza definicja jest wprawdzie nieco dluzsza od tej wyrazonej
przy pomocy define-macro, ale wydaje sie tez bardziej zrozumiala.
Pojawil sie w niej wreszcie identyfikator specjalny "else", co stwarza
okazje do wyjasnienia jego roli.
Gdyby slowo kluczowe "else" nie znajdowalo sie na liscie identyfikatorow
specjalnych, to dwa pierwsze wzorce w definicji makra bylyby nierozroznialne
-- dla jezyka wzorcow w ogolnosci nie ma znaczenia, czy wystepujacy
w nim symbol ma postac "condition", "else" czy jakakolwiek inna (za
wyjatkiem symbolu "...").
Umieszczenie slowka "else" na liscie identyfikatorow specjalnych sprawia,
ze pierwszy wzorzec zostanie dopasowany tylko wtedy, gdy w przetwarzanej
formie wystapi literalnie symbol "else".
** PROBA PONOWNEGO ZDEFINIOWANIA FORMY "while"
W pierwszej chwili moglibysmy chciec zdefiniowac nasze makro "while"
nastepujaco:
: (define-syntax-rule (while condition actions ...)
: (call/cc
: (lambda (break)
: (define (LOOP)
: (if condition
: (begin actions ... (LOOP))))
: (LOOP))))
Niestety, okazuje sie, ze oprocz tego, ze transformator makr ukrywa przed nami
identyfikator "LOOP", to nie mamy tez sposobu, zeby dostac sie z zewnatrz
do identyfikatora "break".
Co gorsza, system makr syntax-rules robi wszystko, zeby nam ten dostep
uniemozliwic, i chociaz w ogolnosci istnieja sposoby na omijanie higieny
makr w tym systemie, sa one bardzo trudne, a ich zastosowanie powoduje
powstanie kodu, ktorego analiza jest bardzo trudna.
Jedyny "prosty" sposob obejscia tego problemu polega na tym, zeby uczynic
slowo kluczowe sluzace do przerwania petli jednym z argumentow makra:
: (define-syntax-rule (while/break break condition actions ...)
: (call/cc
: (lambda (break)
: (define (LOOP)
: (if condition
: (begin actions ... (LOOP))))
: (LOOP))))
Trudno jednak uznac takie rozwiazanie za satysfakcjonujace. Znalezienie
lepszego rozwiazania bedzie od nas wymagalo wyjscia poza system makr
"syntax-rules" i przyjrzenia sie jego architektonice.
Następne wpisy z tego wątku
- 12.11.14 05:42 A.L.
- 12.11.14 09:02 g...@g...com
- 12.11.14 11:00 firr
- 12.11.14 11:19 g...@g...com
- 12.11.14 11:41 firr
- 12.11.14 12:11 g...@g...com
- 12.11.14 12:13 firr
- 12.11.14 12:20 g...@g...com
- 12.11.14 12:22 firr
- 12.11.14 12:31 firr
- 12.11.14 12:34 firr
- 13.11.14 11:40 firr
Najnowsze wątki z tej grupy
- 7. Raport Totaliztyczny: Sprawa Qt Group wer. 424
- TCL - problem z escape ostatniego \ w nawiasach {}
- Nauka i Praca Programisty C++ w III Rzeczy (pospolitej)
- testy-wyd-sort - Podsumowanie
- Tworzenie Programów Nieuprzywilejowanych Opartych Na Wtyczkach
- Do czego nadaje się QDockWidget z bibl. Qt?
- Bibl. Qt jest sztucznie ograniczona - jest nieprzydatna do celów komercyjnych
- Co sciaga kretynow
- AEiC 2024 - Ada-Europe conference - Deadlines Approaching
- Jakie są dobre zasady programowania programów opartych na wtyczkach?
- sprawdzanie słów kluczowych dot. zła
- Re: W czym sie teraz pisze programy??
- Re: (PDF) Surgical Pathology of Non-neoplastic Gastrointestinal Diseases by Lizhi Zhang
- CfC 28th Ada-Europe Int. Conf. Reliable Software Technologies
- Młodzi programiści i tajna policja
Najnowsze wątki
- 2024-12-01 "Chciałem zamówić kurs tym"
- 2024-11-30 Windykatorzy ścigają spadkobierców z mandat nieboszczyka za przekroczenie prędkości???
- 2024-11-30 Łódź => Technical Artist <=
- 2024-11-30 Lublin => Inżynier Serwisu Sprzętu Medycznego <=
- 2024-11-30 Warszawa => Microsoft Dynamics 365 Business Central Developer <=
- 2024-11-30 Bieruń => Team Lead / Tribe Lead FrontEnd <=
- 2024-11-30 Zielona Góra => Senior PHP Symfony Developer <=
- 2024-11-30 Gdańsk => Specjalista ds. Sprzedaży <=
- 2024-11-30 Lublin => Spedytor międzynarodowy <=
- 2024-11-30 Warszawa => Mid IT Recruiter <=
- 2024-11-30 Warszawa => Fullstack Developer <=
- 2024-11-30 Żerniki => Dyspozytor Międzynarodowy <=
- 2024-11-30 Warszawa => System Architect (background deweloperski w Java) <=
- 2024-11-30 Katowice => Key Account Manager (ERP) <=
- 2024-11-30 Immatrykulacja...