eGospodarka.pl
eGospodarka.pl poleca

eGospodarka.plGrupypl.comp.programmingMakra w jezyku SchemeRe: Makra w jezyku Scheme
  • X-Received: by 10.140.34.14 with SMTP id k14mr5032qgk.8.1415540051226; Sun, 09 Nov
    2014 05:34:11 -0800 (PST)
    X-Received: by 10.140.34.14 with SMTP id k14mr5032qgk.8.1415540051226; Sun, 09 Nov
    2014 05:34:11 -0800 (PST)
    Path: news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed.pionier.net.pl!news.glorb.com!
    h15no953152igd.0!news-out.google.com!u1ni1qah.0!nntp.google.com!i13no328782qae.
    0!postnews.google.com!glegroupsg2000goo.googlegroups.com!not-for-mail
    Newsgroups: pl.comp.programming
    Date: Sun, 9 Nov 2014 05:34:11 -0800 (PST)
    In-Reply-To: <f...@g...com>
    Complaints-To: g...@g...com
    Injection-Info: glegroupsg2000goo.googlegroups.com; posting-host=46.186.78.6;
    posting-account=f7iIKQoAAAAkDKpUafc-4IXhmRAzdB5r
    NNTP-Posting-Host: 46.186.78.6
    References: <c...@g...com>
    <f...@g...com>
    User-Agent: G2/1.0
    MIME-Version: 1.0
    Message-ID: <6...@g...com>
    Subject: Re: Makra w jezyku Scheme
    From: g...@g...com
    Injection-Date: Sun, 09 Nov 2014 13:34:11 +0000
    Content-Type: text/plain; charset=ISO-8859-1
    Xref: news-archive.icm.edu.pl pl.comp.programming:206866
    [ ukryj nagłówki ]

    * POTRZEBA NOWYCH FORM SPECJALNYCH

    ** UWAGI WSTEPNE

    Dotychczas omowilem zaledwie 6 podstawowych form specjalnych potrzebnych
    do tego, zeby budowac zlozone programy. Brakuje tutaj jednak wielu rodzajow
    wyrazen, ktore znamy z innych jezykow programowania, takich jak mozliwosc
    wprowadzania zmiennych pomocniczych w ciele funkcji, albo logicznych operatorow
    koniunkcji i alternatywy (negacje moglibysmy bowiem zdefiniowac jako funkcje:
    (define not (lambda (x) (if x #f #t)))).

    W tym celu musielibysmy dysponowac jeszcze przynajmniej jedna forma specjalna,
    pozwalajaca nam na definiowanie nowych form specjalnych. Nie powinno to byc
    trudne, poniewaz -- jak sobie powiedzielismy na poczatku -- programy w lispie
    sa listami symboli, wiec wystarczyloby zmodyfikowac reguly ewaluacji w taki
    sposob, zeby wszystkie nowe formy specjalne byly przed wykonaniem przeksztalcane
    do postaci zawierajacej wylacznie funkcje i pierwotne formy specjalne.

    Taka procedura przeksztalcajaca jakies wyrazenie (liste) w inne wyrazenie
    (liste) nazywana jest *makrem*. Zanim poznamy szczegoly dotyczace tego,
    jak mozna definiowac makra, sprobujemy sie zastanowic, jak bysmy mogli
    chciec uzywac danego wyrazenia, oraz jak moglaby wygladac nasza forma
    wynikowa. Jedyne ograniczenie jest takie, ze nasza skladnia musi byc
    zgodna ze skladnia lispa.

    UWAGA! WSZYSTKIE MAKRA PRZEDSTAWIONE W TYM ROZDZIALE SA CZESCIA JEZYKA
    SCHEME I NIE TRZEBA ICH DEFINIOWAC. GDYBY CZYTELNIK ZECHCIAL ZDEFINIOWAC
    SWOJE WERSJE PONIZSZYCH FORM SPECJALNYCH, ZALECAM NADANIE IM INNYCH NAZW.

    ** ZMIENNE LOKALNE -- FORMA "let"

    Czasem wygodnie jest przechowac dana wartosc w zmiennej pomocniczej.

    : (let ((x^2 (* x x))
    : (y^2 (* y y)))
    : (/ (- x^2 y^2) (+ x^2 y^2)))

    W tym wypadku uzylismy zmiennych lokalnych o nazwach "x^2" i "y^2"
    do zapamietania wyniku obliczenia dzialan (* x x) i (* y y) (przy zalozeniu,
    ze zmienne "x" i "y" sa zwiazane w kontekscie wyrazenia). W przeciwnym
    razie musielibysmy dwukrotnie wyliczyc wynik mnozenia, zas glowne wyrazenie
    staloby sie rozwleklejsze:

    : (/ (- (* x x) (* y y)) (+ (* x x) (* y y)))

    Zauwazmy, ze pozadany efekt mozemy uzyskac przy pomocy formy "lambda":

    : ((lambda(x^2 y^2)(/ (- x^2 y^2) (+ x^2 y^2))) x y)

    Dlatego chcielibysmy, zeby formy postaci

    : (let ((nazwa wartosc) ...) cialo + ...)

    byly przed ewaluacja transformowane do postaci

    : ((lambda (nazwa ...) cialo + ...) wartosc ...)

    ** LOGICZNE SPOJNIKI "and" i "or"

    Moglibysmy zdefiniowac operatory logiczne "and" i "or" jako funkcje:

    : (define and2 (lambda (p q) (if p q #f)))
    : (define or2 (lambda (p q) (if p p q)))

    Z taka definicja sa jest jednak pewien problem: mianowicie, nasz operator
    ewaluuje wsztstkie swoje argumenty, choc teoretycznie jezeli pierwszy
    argument ma wartosc falszu, to moglibysmy zaniechac ewaluacji kolejnych
    argumentow (taka metoda ewaluacji nosi nazwe "short-circit evaluation"
    albo "McCarthy evaluation", i implementuja je wlasciwie wszystkie
    wspolczesnie uzywane jezyki programowania).

    Moglibysmy zatem zyczyc sobie, zeby kod

    : (or p q)

    byl przed wykonaniem transformowany do postaci

    : (if p p q)

    jednak wowczas problemem byloby to, ze wyrazenie "p" -- o ile jego wartoscia
    nie bylby falsz logiczny -- byloby ewaluowane dwukrotnie. Moglibysmy temu
    zapobiec transformujac nasza alternatywe do postaci

    : (if p #t q)

    albo, pragnac zachowac wartosc wyniku ewaluacji, przeksztalcic ja nastepujaco:

    : (let ((T p)) (if T T q))

    gdzie T jest symbolem niewystepujacym w q. Moglibysmy tez uogolnic operator
    "or" w taki sposob, zeby mogl przyjmowac dowolnie wiele argumentow, i zeby
    wyrazenia postaci

    : (or p q r ...)

    byly transformowane do postaci

    : (let ((T p)) (if T T (or q r ...)))

    Analogicznie moglibysmy oczekiwac, zeby wyrazenia postaci

    : (and p q)

    byly przed ewaluacja przeksztalcane do postaci

    : (if p q #f)

    i ogolniej, interpretowac wyrazenia

    : (and p q r ...)

    jako

    : (if p q (and r ...))

    ** FORMA SPECJALNA "cond"

    Wiekszosc popularnych jezykow programowania posiada specjalna konstrukcje
    dla wyboru sekwencyjnego, np.

    : if warunek { dzialania ... }
    : else if inny_warunek { inne_dzialania ... }
    : ...
    : else { ostateczne_dzialania ... }

    W Schemie moglibysmy uzyc kaskady instrukcji "if":

    : (if warunek
    : (begin dzialania ...)
    : (if inny_warunek
    : (begin inne_dzialania ...)
    : ... (begin ostateczne_dzialania ...) ...))

    Taki kaskadowy kod osiaga jednak dosc szybko duzy poziom zaglebienia
    i staje sie nieprzyjemny do czytania. Definiujac Lispa, John McCarthy
    wprowadzil forme "cond" o nastepujacej skladni:

    : (cond (warunek dzialania ...)
    : (inny_warunek inne_dzialania ...)
    : ...
    : (else ostateczne_dzialania ...))

    "else" jest w kontekscie formy "cond" symbolem specjalnym. Powyzszy kod
    moglby byc przeksztalcony do postaci rekurencyjnej:

    : (if warunek
    : (begin dzialania ...)
    : (cond (inny_warunek inne_dzialania ...)
    : ...
    : (else ostateczne_dzialania ...)))

    zas forme z jednym warunkiem (przypadek brzegowy), np.

    : (cond (ostatni_warunek srodki_ostateczne ...))

    moglibysmy interpretowac rownowaznie z

    : (if ostatni_warunek (begin srodki_ostateczne ...))

    ** PETLA "while" oraz funkcja "call/cc"

    W sekcji objasniajacej dzialanie formy specjalnej "if" podalem przyklad
    funkcji obliczajacej silnie. Podany przeze mnie kod byl czysto funkcyjny,
    dzieki czemu mozna bylo dokonac analizy jego dzialania za pomoca prostych
    podstawien.

    Osoby przyzwyzajone do stylu imperatywnego moglyby zapewne chciec zdefiniowac
    silnie nastepujaco:

    : (define !!
    : (lambda (n)
    : (let ((result 1))
    : (while (> n 0)
    : (set! result (* result n))
    : (set! n (- n 1)))
    : result)))

    Uzylismy tutaj petli "while", ktorej zasada dzialania powinna byc znana
    wiekszosci programistow. Moglibysmy ja wyrazic przy pomocy wprowadzonych
    dotychczas srodkow. Mianowicie chcielibysmy, zeby kod

    : (while warunek dzialania ...)

    byl transformowany do postaci

    : (begin
    : (define LOOP
    : (lambda ()
    : (if warunek
    : (begin dzialania ... (LOOP)))))
    : (LOOP))

    W tym kontekscie warto omowic dzialanie "call/cc". Otoz jest to specjalna
    procedura, ktora pobiera jeden argument, bedacy lambda-wyrazeniem od jednego
    argumentu. Tym argumentem jest tzw. kontunuacja -- procedura (mogaca przyjmowac
    dowolnie wiele argumentow), ktorej wywolanie spowoduje przerwanie wykonywania
    biezacej funkcji i przekazanie sterowania do wyrazenia nastepujacego
    bezposrednio po wywolaniu call/cc. Owo wyjasnienie moze brzmiec niezrozumiale,
    dlatego warto posluzyc sie przykladem:

    : (call/cc
    : (lambda (return)
    : (display "a")
    : (return)
    : (display "b")))
    : (display "c")

    Powyzsze wywolanie wypisze ciag znakow "ac" (wypisanie "b" zostanie pominiete).
    Procedure call/cc mozemy zastosowac do zaimplementowania instrukcji "break",
    znanej z takich jezykow jak C czy Python. Na przyklad, moglibysmy transformowac
    powyzsza petle "while" do postaci

    : (call/cc
    : (lambda (break)
    : (define LOOP
    : (lambda ()
    : (if warunek
    : (begin dzialania ... (LOOP)))))
    : (LOOP)))

    Kontynuacje (czyli pojecie, pod ktore podpadaja takie konstrukcje z jezykow
    imperatywnych, jak "exit" w kontekscie programu, "return", "yield" czy "goto"
    w kontekscie funkcji, "continue" i "break" w kontekscie petli oraz "throw" i
    "catch" w kontekscie wyjatkow) maja w jezyku Scheme status obiektow, ktore
    moga byc przekazywane, zachowywane i wielokrotnie wywolywane. Spora ogolnosc
    kontynuacji byla przedmiotem krytyki z powodu pewnych trudnosci implementacji,
    duzego zuzycia zasobow i niskiej wydajnosci, co sklonilo teoretykow jezykow
    programowania do wprowadzenia roznych wariantow kontynuacji o ograniczonym
    zasiegu. Osoby zaintersowane szczegolowa dyskusja odsylam do strony
    http://okmij.org/ftp/continuations/against-callcc.ht
    ml

    Tymczasem dodanie do petli "while" instrukcji "continue" pozostawiam jako
    cwieczenie dla ciekawskich.

    ** INNE FORMY SPECJALNE

    Podana powyzej lista form specjalnych jest podzbiorem form specjalnych
    definiowanych przez standard Scheme'a. Oprocz nich definiuje sie rowniez
    m.in. formy "let*", "letrec" czy "do". Moim celem nie jest jednak omowienie
    wszystkich form obecnych w jezyku Scheme, ale zarysowanie, w jaki sposob
    obecnosc makr zwieksza sile wyrazu jezyka, oraz zademonstrowanie roznych
    podejsc do tego, w jaki sposob mozna owe formy specjalne definiowac.

    Pod koniec tekstu podam kilka mniej trywialnych zastosowan dla makr,
    a tymczasem przejde do omowienia mozliwych (praktycznych) implementacji.

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: