eGospodarka.pl
eGospodarka.pl poleca

eGospodarka.plGrupypl.comp.programmingMakra higieniczne w jezyku SchemeRe: Makra higieniczne w jezyku Scheme
  • 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.

Podziel się

Poleć ten post znajomemu poleć

Wydrukuj ten post drukuj


Następne wpisy z tego wątku

Najnowsze wątki z tej grupy


Najnowsze wątki

Szukaj w grupach

Eksperci egospodarka.pl

1 1 1

Wpisz nazwę miasta, dla którego chcesz znaleźć jednostkę ZUS.

Wzory dokumentów

Bezpłatne wzory dokumentów i formularzy.
Wyszukaj i pobierz za darmo: