Scheme to minimalistyczny dialekt języka programowania Lisp, zaprojektowany w celu ułatwienia stylu programowania funkcyjnego przy jednoczesnym promowaniu prostej i czystej składni. Charakteryzuje się użyciem nawiasów, potężnym systemem makr oraz silnym naciskiem na rekurencję i procedury pierwszej klasy. Język ten zachęca do paradygmatu programowania funkcyjnego i wspiera różne techniki programowania, w tym programowanie funkcyjne, imperatywne i logiczne.
Scheme został stworzony w latach 70. przez Geralda Jaya Sussmana i Guya L. Steele'a Jr. w Massachusetts Institute of Technology (MIT). Został opracowany jako próba uproszczenia i poprawienia oryginalnego języka Lisp, który z biegiem czasu stał się skomplikowany. Motywacją do stworzenia Scheme było stworzenie języka, który byłby łatwiejszy do wdrożenia i nauczania, a jednocześnie wystarczająco potężny, aby wyrażać złożone idee.
Na przestrzeni lat Scheme przeszedł różne rewizje i wysiłki standaryzacyjne. Najbardziej znaczącymi z tych standardów są RnRS (Revisedn Reports on the Algorithmic Language Scheme), które sformalizowały język i jego cechy. Społeczność Scheme kontynuuje rozwój języka, prowadząc do nowszych standardów, takich jak R6RS i R7RS, które wprowadziły nowe funkcje i ulepszenia.
Obecnie Scheme pozostaje popularny w środowiskach akademickich, szczególnie w edukacji informatycznej, ze względu na swoją elegancję i czystą składnię. Jego wpływ można dostrzec w wielu nowoczesnych językach programowania i paradygmatach. Scheme jest często kojarzony ze społecznością programowania funkcyjnego i zainspirował języki takie jak Clojure i Racket, które oparte są na jego zasadach i rozszerzają jego możliwości.
Scheme używa w pełni nawiasowej składni, w której kod jest pisany w notacji prefiksowej; na przykład operacja dodawania jest zapisywana jako (+ 1 2)
.
Funkcje w Scheme są obywatelami pierwszej klasy, co oznacza, że mogą być przekazywane jako argumenty lub zwracane z innych funkcji. Przykład:
(define (make-adder x)
(lambda (y) (+ x y)))
(define add5 (make-adder 5))
(add5 10) ; zwraca 15
Scheme wspiera optymalizację wywołań końcowych, pozwalając funkcjom na ponowne użycie bieżącej ramki stosu dla wywołań rekurencyjnych, które występują w pozycji końcowej, zapobiegając przepełnieniu stosu.
Scheme pozwala na użycie kontynuacji, umożliwiając programistom uchwycenie bieżącego stanu obliczeń i manipulację przepływem sterowania:
(call-with-current-continuation
(lambda (exit)
(exit 'done)))
Scheme ma potężny system makr, który pozwala programistom na tworzenie rozszerzeń składniowych. Proste makro można zdefiniować w następujący sposób:
(define-syntax my-if
(syntax-rules ()
((_ test then else)
(if test then else))))
Scheme wspiera abstrakcję danych poprzez struktury takie jak listy, pary i funkcje wyższego rzędu, co umożliwia efektywną manipulację danymi.
Scheme stosuje zakres leksykalny, w którym zakres zmiennej jest określany przez jej fizyczną lokalizację w kodzie źródłowym. Prowadzi to do przewidywalnego zachowania w odniesieniu do powiązań zmiennych.
Scheme jest językiem o dynamicznym typowaniu, co pozwala zmiennym na przechowywanie wartości dowolnego typu bez potrzeby jawnych deklaracji typów.
Jako dialekt Lispa, Scheme jest zoptymalizowany do przetwarzania list, co ułatwia wykonywanie operacji na listach:
(define my-list (list 1 2 3 4))
(car my-list) ; zwraca 1
(cdr my-list) ; zwraca (2 3 4)
Funkcje w Scheme mogą być definiowane rekurencyjnie w pozycji końcowej, aby zoptymalizować użycie pamięci i wydajność. Na przykład:
(define (factorial n)
(define (fact-helper n acc)
(if (zero? n)
acc
(fact-helper (- n 1) (* n acc))))
(fact-helper n 1))
Aplikacje w Scheme można zazwyczaj budować przy użyciu środowiska uruchomieniowego wybranej implementacji. Na przykład w Racket można użyć narzędzia wiersza poleceń racket
, aby uruchomić pliki .rkt
lub skompilować je do plików wykonywalnych.
Dostępnych jest kilka kompilatorów i interpreterów dla Scheme, w tym:
Scheme jest szeroko stosowany w akademickim nauczaniu zasad programowania oraz jako narzędzie do badań w informatyce. Ma zastosowania w sztucznej inteligencji, projektowaniu języków i skryptowaniu, a także w różnych dziedzinach wymagających obliczeń symbolicznych lub złożonej manipulacji danymi.
Oba języki wspierają styl programowania funkcyjnego, ale Python kładzie nacisk na czytelność i prostotę, podczas gdy Scheme koncentruje się na potężnych i zwięzłych wyrażeniach. Składnia Scheme, bogata w nawiasy, może być postrzegana jako bariera dla początkujących, podczas gdy składnia Pythona oparta na wcięciach jest zazwyczaj bardziej dostępna.
JavaScript również wspiera programowanie funkcyjne i ma funkcje pierwszej klasy. Jednak JavaScript ma system obiektowy oparty na prototypach, podczas gdy Scheme opiera się na paradygmatach funkcyjnych. System makr Scheme oferuje możliwości, które nie są obecne w JavaScript.
C to język niskiego poziomu, statycznie typowany, zaprojektowany do programowania systemowego, podczas gdy Scheme jest językiem wysokiego poziomu i dynamicznie typowanym. C koncentruje się na wydajności i manipulacji na poziomie sprzętowym, podczas gdy Scheme kładzie nacisk na abstrakcję i teoretyczne aspekty obliczeń.
Oba języki mają swoje korzenie w programowaniu funkcyjnym. Haskell używa bardziej sztywnego systemu typów i wspiera leniwe ocenianie, podczas gdy dynamiczne typowanie i eager evaluation w Scheme pozwalają na bardziej elastyczne programowanie kosztem nieco gorszej wydajności.
Ruby to język programowania obiektowego, który łączy cechy programowania funkcyjnego, podczas gdy Scheme jest czysto funkcyjny. Składnia Ruby jest bardziej rozbudowana, podczas gdy prostota Scheme wynika z jego minimalistycznego projektu.
Tłumaczenie kodu Scheme na inne języki można ułatwić za pomocą narzędzi, które rozumieją zarówno składnię Scheme, jak i język docelowy. Niektóre przydatne narzędzia do tłumaczenia kodu źródłowego to:
Chicken Scheme zapewnia kompilator, który może tłumaczyć kod Scheme na C, co umożliwia wykorzystanie bibliotek C i tworzenie wydajnych plików wykonywalnych.
Racket, będący pochodną Scheme, ma wbudowane możliwości transformacji i kompilacji kodu do JavaScript, co ułatwia wdrażanie aplikacji internetowych.
Gambit Scheme może kompilować kod Scheme do C i natywnych plików wykonywalnych, oferując wydajne opcje tłumaczenia dla aplikacji krytycznych pod względem wydajności.