Programowanie funkcyjne/Scheme: Różnice pomiędzy wersjami
Linia 65: | Linia 65: | ||
=== Wyrażenia warunkowe === | === Wyrażenia warunkowe === | ||
<p align="justify"> | <p align="justify"> | ||
Mamy dwa rodzaje wyrażeń warunkowych: <tt> | Mamy dwa rodzaje wyrażeń warunkowych: <tt>cond</tt> i <tt>if</tt>. | ||
Wyrażenie warunkowe <tt>cond</tt> ma postać kombinacji składającej się ze | Wyrażenie warunkowe <tt>cond</tt> ma postać kombinacji składającej się ze | ||
słowa kluczowego <tt>cond</tt> oraz pewnej liczby ''klauzul''. | słowa kluczowego <tt>cond</tt> oraz pewnej liczby ''klauzul''. | ||
Linia 77: | Linia 77: | ||
* obliczaniu dozorów kolejnych klauzul, aż do napotkania prawdziwego dozoru, | * obliczaniu dozorów kolejnych klauzul, aż do napotkania prawdziwego dozoru, | ||
* obliczeniu następnika klauzuli, której dozór jest prawdziwy. | * obliczeniu następnika klauzuli, której dozór jest prawdziwy. | ||
W ostatniej klauzuli, jako dozór można podać słowo kluczowe <tt>else</tt>. | |||
Jest ono równoważne dozorowi, który jest zawsze spelniony. | |||
</p> | </p> | ||
<wyrażenie> ::= <u>(cond</u> { <u>(</u> <wyrażenie> <wyrażenie> <u>)</u> }<sup>+</sup> <u>)</u> | |||
<p align="justify"> | <p align="justify"> | ||
Linia 85: | Linia 89: | ||
Trzeci element to wyrażenie, które jest obliczane gdy warunek jest prawdziwy, | Trzeci element to wyrażenie, które jest obliczane gdy warunek jest prawdziwy, | ||
a czwarty to wyrażenie, które jest obliczane gdy warunek jest fałszywy. | a czwarty to wyrażenie, które jest obliczane gdy warunek jest fałszywy. | ||
</p> | </p> | ||
<wyrażenie> ::= <u>( | <wyrażenie> ::= <u>(if</u> <wyrażenie> <wyrażenie> <wyrażenie> <u>)</u> | ||
Wyrażenie postaci: | |||
<center><math> | |||
\mathtt{(if}\ w\ v_1\ v_2\mathtt{)} | |||
</math></center> | |||
jest równoważne: | |||
<center><math> | |||
\mathtt{(cond\ (}w\ v_1\mathtt{)\ (else}\ v_2\mathtt{))} | |||
</math></center> | |||
<p align="justify"> | |||
Oczywiście, wyrażenia warunkowe <tt>cond</tt> i <tt>if</tt> są formami specjalnymi, | |||
gdyż nie dałoby się ich zdefiniować jako procedur. | |||
</p> | |||
== Definicje == | == Definicje == |
Wersja z 22:43, 17 gru 2006
Wstęp
W dotychczasowych wykładach poznawaliśmy i używaliśmy języka Ocaml. Ocaml (Objective Caml) to dialekt języka ML. Wśród dialektów ML-a jest to język bogaty, ze względu na to, że zawiera:
- bogaty zestaw bibliotek,
- programowanie obiektowe,
- system modułów i funktorów, wraz z funktorami wyższych rzędów.
Tak jak inne dialekty ML-a, Ocaml charakteryzuje się:
- ścisłą statyczną kontrolą typów,
- polimorfizmem, oraz
- tym, że zawiera konstrukcje imperatywne.
W tym i kolejnym wykładzie zobaczymy przedstawicieli innych rodzin funkcyjnych języków programowania. W tym wykładzie poznamy język Scheme -- dialekt Lispu. Jest to język o bardzo prostej, wręcz minimalistycznej składni. Tak jak Ocaml, zawiera konstrukcje imperatywne. Natomiast charakteryzuje się dynamiczną kontrolą typów.
Kombinacje i wyrażenia
Podstawową konstrukcją składniową w Scheme'ie jest kombinacja. Jest to sekwencja wartości ujętych w nawiasy. Pierwsza z tych wartości musi być procedurą. Kolejne wartości stanowią argumenty.
Scheme jest językiem z gorliwym obliczaniem wartości argumentów. Obliczenie wartości kombinacji polega na:
- obliczeniu wartości wszystkich elementów kombinacji (zarówno procedury, jak i jej argumentów),
- zastosowaniu procedury do obliczonych wartości argumentów.
Wyrażenia, nazywane również S-wyrażeniami, budujemy używając kombinacji, nazwanych wartości i stałych.
<wyrażenie> ::= <stała> | <kombinacja> <kombinacja> ::= ( { <wyrażenie> }+ ) <stała> ::= <identyfikator> | <liczba> | ...
Do pewnego stopnia, wyrażenia zapisujemy jak w notacji polskiej, tzn. najpierw operacja, a potem argumenty. Jednak inaczej niż w notacji polskiej, nawiasy otaczające kombinacje są konieczne. Niektóre procedury potrafią przyjmować różne liczby argumentów. Jest tak np. z operacjami arytmetycznymi. Nawiasy wyznaczają dokładnie listę argumentów w wyołaniu procedury.
Przykład [Wyrażenia]
42 (+ 36 6) (* 3 14) (- 100 58) (- (* 1 2 3 4 5) (/ (* (+ 6 7) 8 9) 12)) (/ (silnia 7) (silnia 5)) (/ 596.4 14.2)
Wyrażenia warunkowe
Mamy dwa rodzaje wyrażeń warunkowych: cond i if. Wyrażenie warunkowe cond ma postać kombinacji składającej się ze słowa kluczowego cond oraz pewnej liczby klauzul. Każda z klauzul to para wyrażeń ujętych w nawiasy. Pierwsze z wyrażeń musi być warunkiem logicznym -- jest ono nazywane dozorem. Drugie wyrażenia w klauzulach nazywamy następnikami.
Obliczenie wyrażenia warunkowego cond polega na:
- obliczaniu dozorów kolejnych klauzul, aż do napotkania prawdziwego dozoru,
- obliczeniu następnika klauzuli, której dozór jest prawdziwy.
W ostatniej klauzuli, jako dozór można podać słowo kluczowe else. Jest ono równoważne dozorowi, który jest zawsze spelniony.
<wyrażenie> ::= (cond { ( <wyrażenie> <wyrażenie> ) }+ )
Wyrażenia warunkowe if mają postać kombinacji czterech elementów, z których pierwszy to słowo kluczowe if. Drugi element kombinacji to warunek. Trzeci element to wyrażenie, które jest obliczane gdy warunek jest prawdziwy, a czwarty to wyrażenie, które jest obliczane gdy warunek jest fałszywy.
<wyrażenie> ::= (if <wyrażenie> <wyrażenie> <wyrażenie> )
Wyrażenie postaci:
jest równoważne:
Oczywiście, wyrażenia warunkowe cond i if są formami specjalnymi, gdyż nie dałoby się ich zdefiniować jako procedur.
Definicje
Nowe nazwane wartości możemy definiować za pomocą formy specjalnej define. Ma ona postać kombinacji trzech elementów, z których pierwszy to słowo kluczowe define. Jeżeli drugi element jest identyfikatorem, to jest to definicja stałej, a jej wartość określa trzeci element. Drugi element może też być kombinacją złożoną z kilku identyfikatorów. Wówczas jest to definicja procedury -- pierwszy z identyfikatorów to nazwa definiowanej procedury, a kolejne to parametry formalne definiowanej procedury. Trzeci element definicji to treść procedury. Definicje procedur mogą być rekurencyjne (i nie wymaga to dodatkowego zaznaczenia, jak w Ocamlu).
<definicja> ::= (define <identyfikator> <wyrażenie> ) | (define ( { <identyfikator> }+ ) <wyrażenie> )
Przykład [Definicje stałych]
Oto przykładowe definicje stałych
(define a 6) (define b (+ a 1)) (* a b) 42
Podobnie jak w Ocamlu, kolejne definicje przysłaniają już zdefiniowane stałe:
(define a 2) (define b (* 2 a)) (define a (* a b)) a 8
Przykład [Definicje procedur]
(define (silnia n) (if (<= n 1) 1 (* n (silnia (- n 1)))))