Особенности ракетки
Racket находится в стадии активной разработки как средство исследования языков программирования с середины 1990-х годов и за эти годы накопил множество функций. В этой статье описываются и демонстрируются некоторые из этих функций. Обратите внимание, что одной из основных целей разработки Racket является создание новых языков программирования , как языков, специфичных для предметной области , так и совершенно новых языков. [1] Поэтому некоторые из следующих примеров приведены на разных языках, но все они реализованы в Racket. Пожалуйста, обратитесь к основной статье для получения дополнительной информации.
Основная реализация Racket очень гибкая. Даже без использования диалектов он может функционировать как полнофункциональный язык сценариев, способный работать как с собственным графическим интерфейсом пользователя (GUI) Windows, так и без него, а также решать задачи, от создания веб-сервера до графики.
Поддержка во время выполнения [ править ]
, хвостовые вызовы, безопасность космическая Сбор мусора
Racket может использовать три разных сборщика мусора :
- консервативный сборщик мусора Boehm Первоначально использовался . Однако консервативный сбор непрактичен для длительно выполняющихся процессов, таких как веб-сервер, поскольку такие процессы имеют тенденцию к медленной утечке памяти. Кроме того, бывают патологические случаи, когда у консервативного сборщика происходит утечка памяти настолько быстро, что запуск некоторых программ становится невозможным. Например, при обходе бесконечного списка единственная консервативная ошибка сохранения указателя приводит к сохранению всего списка в памяти, что приводит к быстрому переполнению доступной памяти. В рэкет-сообществе этого коллекционера часто называют «CGC».
- SenoraGC — альтернативный консервативный сборщик мусора, предназначенный в основном для отладки и трассировки памяти.
- Менеджер движущейся памяти (он же «3m») — это точный сборщик мусора, который является сборщиком по умолчанию в Racket с 2007 года. Этот сборщик является сборщиком поколений и поддерживает учет памяти через хранителей (см. ниже). Коллектор реализован как исходный преобразователь C, который сам написан на Racket. Поэтому процесс сборки использует консервативный сборщик для начальной загрузки .
Как и все реализации семейства Scheme , Racket реализует полное хвостовых вызовов устранение . Racket идет дальше: язык становится полностью безопасным для пространства благодаря анализу переменных в реальном времени . Это дополняет точный сборщик мусора, и в некоторых случаях, как в случае с Lazy Racket, эти две функции имеют решающее значение для правильного выполнения. Это в дополнение к дальнейшим оптимизациям компилятора, таким как поднятие лямбда-выражений и своевременная компиляция .
Системный интерфейс и скрипты [ править ]
Системный интерфейс Racket включает в себя асинхронный неблокирующий ввод-вывод , зеленые потоки , каналы синхронизации, семафоры, подпроцессы и сокеты протокола управления передачей (TCP).
Следующая программа запускает «эхо-сервер» на порту 12345.
#lang racket
(define listener (tcp-listen 12345))
(let echo-server ()
;; create a TCP server
(define-values (in out) (tcp-accept listener))
;; handle an incoming connection in a (green) thread
(thread (λ () (copy-port in out) (close-output-port out)))
;; and immediately loop back to accept more clients
(echo-server))
Сочетание динамической компиляции и богатого системного интерфейса делает Racket функциональным языком сценариев, подобным Perl или Python .
В следующем примере демонстрируется обход дерева каталогов, начиная с текущего каталога. Он использует in-directory
функция для построения последовательности, которая обходит дерево. for
форма связывает path
к каждому пути в последовательности, и regexp-match?
проверяет эти пути на соответствие заданному шаблону регулярного выражения .
#lang racket
;; Finds Racket sources in all subdirs
(for ([path (in-directory)]) ; iterate over the current tree
(when (regexp-match? #rx"[.]rkt$" path)
(printf "source file: ~a\n" path)))
В следующем примере используется хеш-таблица для записи ранее просмотренных строк и печати только уникальных.
#lang racket
;; Report each unique line from stdin
(let ([saw (make-hash)])
(for ([line (in-lines)])
(unless (hash-ref saw line #f)
(displayln line))
(hash-set! saw line #t)))
Обе эти программы можно запустить в DrRacket или в командной строке через команду racket
исполняемый файл. Racket игнорирует начальную строку shebang , что позволяет превращать такие программы в исполняемые сценарии. Следующий скрипт демонстрирует это, в дополнение к использованию библиотеки Racket для анализа аргументов командной строки :
#!/usr/bin/env racket
#lang racket
(command-line
#:args (base-dir ext re)
(for ([p (in-directory)]
#:when (regexp-match? (string-append "[.]" ext "$") p)
[(line num) (in-indexed (file->lines p))])
(when (regexp-match? (pregexp re) line)
(printf "~a:~a: ~a~n" p (+ num 1) line))))
Сценарий представляет собой утилиту, подобную grep, принимающую три аргумента командной строки: базовый каталог, расширение имени файла и регулярное выражение (совместимое с Perl). Он сканирует базовый каталог на наличие файлов с заданным суффиксом и печатает строки, соответствующие шаблону регулярного выражения.
Управление ресурсами и песочница [ править ]
В Racket используется концепция «хранителя»: своего рода ценности, которая действует как менеджер ресурсов. Это часто используется на сетевых серверах, где каждое соединение обрабатывается новым хранителем, что упрощает «очистку» всех ресурсов, которые могли остаться открытыми обработчиком (например, открытые порты). Следующий пример расширяет пример «эхо-сервера» таким использованием хранителя:
#lang racket
(define listener (tcp-listen 12345))
;; per-connection handler
(define (handler in out)
(copy-port in out)
(close-output-port out))
(let echo-server ()
(define-values (in out) (tcp-accept listener))
(thread (λ () (let ([c (make-custodian)])
(parameterize ([current-custodian c])
(handler in out)
(custodian-shutdown-all c)))))
(echo-server))
Хранители в сочетании с функцией учета памяти сборщика мусора 3m и несколькими дополнительными параметрами времени выполнения, которые контролируют дополнительные аспекты среды выполнения, позволяют создавать полностью безопасные контексты выполнения в изолированной программной среде. racket/sandbox
библиотека предоставляет такую функциональность простым способом. В следующем примере создается сервер цикла чтения-оценки-печати (REPL) на указанном порту; подключение к этому порту будет выглядеть как обычный REPL Racket, за исключением того, что оценка зависит от различных аспектов защиты песочницы. Например, из этого REPL невозможно получить доступ к файловой системе , создать сетевое соединение, запустить подпроцессы или использовать слишком много времени или памяти. (На самом деле, этот REPL достаточно безопасен, чтобы его можно было разглашать публично.)
#lang racket
(require racket/sandbox)
(define e (make-evaluator 'racket/base))
(let-values ([(i o) (tcp-accept (tcp-listen 9999))])
(parameterize ([current-input-port i]
[current-output-port o]
[current-error-port o]
[current-eval e]
[current-read-interaction (λ (x in) (read in))])
(read-eval-print-loop)
(fprintf o "\nBye...\n")
(close-output-port o)))
Веб и сетевое программирование [ править ]
В следующем примере реализуется веб-сервер с использованием web-server/insta
язык. Каждый раз при подключении к серверу start
Функция вызывается, чтобы получить HTML для отправки обратно клиенту.
#lang web-server/insta
;; A tiny "hello world" web server
(define (start request)
(response/xexpr '(html (body "Hello World"))))
Racket также включает в себя функции, необходимые для написания скраперов и роботов. В качестве примера следующая функция выводит результаты Google по строке поиска.
#lang racket
;; Simple web scraper
(require net/url net/uri-codec)
(define (let-me-google-that str)
(let* ([g "http://www.google.com/search?q="]
[u (string-append g (uri-encode str))]
[rx #rx"(?<=<h3 class=\"r\">).*?(?=</h3>)"])
(regexp-match* rx (get-pure-port (string->url u)))))
Библиотека также включает поддержку протоколов, отличных от http:
#lang racket
;; Sending a timed email alert from racket
(require net/sendmail)
(sleep (* (- (* 60 4) 15) 60)) ; wait 3h 45m
(send-mail-message
(getenv "EMAIL") "Parking meter alert!"
(list (getenv "EMAIL")) null null
'("Time to go out and move the car."))
Графика [ править ]
Графические возможности представлены в нескольких вариантах, предназначенных для разных аудиторий. 2htdp/image
библиотека предоставляет удобные функции для построения изображений. Эта библиотека в основном используется студентами курсов, основанных на разработке программ (HtDP). В следующем примере sierpinski
функция определена и вызывается (одновременно) для генерации треугольника Серпинского глубины 8.
#lang racket
;; A picture
(require 2htdp/image)
(let sierpinski ([n 8])
(if (zero? n)
(triangle 2 'solid 'red)
(let ([t (sierpinski (- n 1))])
(freeze (above t (beside t t))))))
Редакторы DrRacket могут содержать изображения, а DrRacket отображает значения изображений так же, как и любые другие типы значений (например, целые числа или списки). Например, запуск вышеуказанной программы фактически отображает треугольник Серпинского, который можно вырезать и вставить в другую программу.
The plot
библиотека создает значения изображений для более зрелой аудитории и потребностей. Например, следующая программа отображает сумму двух (трехмерные) гауссианы как концентрические частично прозрачные поверхности:
#lang racket
;; Visualize a sum of two 3D Gaussians as concentric isosurfaces
;; Note: this example requires Racket 5.2 or later
(require plot)
;; Returns an R x R x R -> R Gaussian function centered at (cx,cy,cz)
(define ((gaussian cx cy cz) x y z)
(exp (- (+ (sqr (- x cx)) (sqr (- y cy)) (sqr (- z cz))))))
;; Lifts + to operate on three-argument functions
(define ((f3+ g h) x y z) (+ (g x y z) (h x y z)))
;; Constructs an image value representing the sum of two Gaussians
(plot3d (isosurfaces3d (f3+ (gaussian 0 0 0) (gaussian 1.5 -1.5 0))
-1 2.5 -2.5 1 -1 1
#:label "g")) ; labeling adds a legend
Здесь isosurfaces3d
функция требует функцию с тремя аргументами в качестве первого аргумента, который каррируется f3+
запасы. Помимо построения значений изображения, plot
также может записывать файлы в форматах переносимой сетевой графики ( PNG ), переносимого формата документов ( PDF ), PostScript и масштабируемой векторной графики ( SVG ).
Программирование графического интерфейса [ править ]
Racket реализует переносимый уровень графического интерфейса , на котором основаны упомянутые выше библиотеки. Он реализуется через собственный Windows интерфейс прикладного программирования ( API ), через Cocoa в macOS , через GTK + в Linux и других. Racket API — это набор инструментов на основе классов, отчасти связанный с wxWidgets , который использовался изначально.
Следующая простая игра в угадайку демонстрирует программирование с использованием набора инструментов GUI. frame%
класс реализует окно верхнего уровня и button%
реализует кнопку. check
определенная здесь функция создает функцию, которая используется для обратного вызова кнопки.
#lang racket/gui
;; A GUI guessing game
(define secret (random 5))
(define f (new frame% [label "Guessing game"])) ; toplevel window
(define t (new message% [parent f]
[label "Can you guess the number I'm thinking about?"]))
(define p (new horizontal-pane% [parent f])) ; horizontal container
(define ((make-check i) btn evt)
(message-box "." (cond [(< i secret) "Too small"]
[(> i secret) "Too big"]
[else "Exactly!"]))
(when (= i secret) (send f show #f))) ; success => close window
(for ([i (in-range 10)]) ; create all buttons
(make-object button% (format "~a" i) p (make-check i)))
(send f show #t) ; show the window to start the application
Графический интерфейс можно запрограммировать вручную или с помощью программы-разработчика графического интерфейса, доступной на PLaneT. [2]
Слайд-шоу [ править ]
слайдов на основе Презентации также можно создавать в Racket с помощью slideshow
язык, очень похожий на Beamer , но с возможностями программирования Racket. Элементами слайдов являются картинки, которые можно комбинировать.
Например, следующая программа отображает в полноэкранном режиме титульный слайд, за которым следует слайд с несколькими изображениями. vc-append
и hc-append
функции объединяют изображения по вертикали и горизонтали соответственно и центрируют по другой оси.
#lang slideshow
(slide
(text "Slideshow" 'roman 56)
(text "Making presentations in Racket"
'roman 40))
(slide
#:title "Some pictures"
(apply vc-append
(for/list ([i 5])
(define (scale+color p c)
(colorize (scale p (/ (add1 i) 5)) c))
(hc-append
(scale+color (filled-rectangle 100 50) "darkblue")
(scale+color (disk 100) "darkgreen")
(scale+color (arrow 100 (/ pi 6)) "darkred")
))))
Пакеты расширений также существуют на PLaneT. [2] например, для включения элементов LaTeX .
Интерфейс внешних функций [ править ]
Racket имеет внешний интерфейс функций , основанный на libffi . Интерфейс позволяет писать небезопасный низкоуровневый C -код, который может выделять память, разыменовывать указатели, вызывать функции в общих библиотеках и отправлять обратные вызовы функциям Racket (с использованием замыканий libffi). Основная реализация представляет собой тонкий слой поверх libffi (написанной на C), а полный интерфейс затем реализуется с помощью кода Racket. на основе Racket В интерфейсе широко используются макросы, что приводит к созданию выразительного языка описания интерфейса . Этот язык имеет ряд полезных функций, таких как единообразное представление функций высшего порядка (что позволяет избежать ошибок, когда обратные вызовы и вызовы различаются), определения структур, похожие на простые структуры Racket, и пользовательские типы функций, которые могут представлять входные и выходные данные. указатели, неявные аргументы (например, аргумент, который предоставляет количество элементов в векторе, который передается в качестве другого аргумента). Используя этот интерфейс для доступа к базовым наборам инструментов графического интерфейса, Racket реализует свой собственный уровень графического интерфейса в Racket. [3]
FFI можно использовать по-разному: от написания полного связующего слоя для библиотеки (как это сделано для привязки OpenGL в Racket ) до быстрого извлечения одной внешней функции. Пример последнего подхода:
#lang racket/base
;; Simple use of the FFI
(require ffi/unsafe)
(define mci-send-string
(get-ffi-obj "mciSendStringA" "Winmm"
(_fun _string [_pointer = #f] [_int = 0] [_pointer = #f]
-> [ret : _int])))
(mci-send-string "play sound.wav wait")
Языковые расширения [ править ]
Наиболее примечательной особенностью Racket является способность создавать новые языки общего назначения и языки предметной области . Это результат объединения ряда важных особенностей:
- гибкая система модулей, которая используется для связывания кода и управления пространством имен,
- обширная система макросов, функционирующая как API-интерфейс компилятора, которая может создавать новые синтаксические формы,
- богатая система времени выполнения, предоставляющая функции, которые могут использовать разработчики языка, такие как (составляемые, разделенные) продолжения, управление ресурсами и т. д.,
- способ указать (и реализовать) синтаксические анализаторы для синтаксиса нового языка.
Система модулей играет важную роль в объединении этих функций и позволяет писать код, охватывающий несколько модулей, каждый из которых может быть написан на разных языках.
Такие языки широко используются в дистрибутиве Racket и в пользовательских библиотеках. На самом деле, создать новый язык настолько просто, что некоторые языки имеют не так уж много применений.
Racket поставляется с рядом полезных языков, некоторые из которых сильно отличаются от языка Racket по умолчанию.
Каракули [ править ]
Scribble, система документации Racket, представлена в виде нескольких языков, которые используются для написания прозы. Он используется для документации Racket, а также для написания книг и статей. На самом деле, это не один «каракульный» язык, а семейство (очень похожих) диалектов, каждый из которых предназначен для разных целей.
Чтобы запустить следующий пример, скопируйте его в DrRacket и нажмите одну из двух появившихся кнопок рендеринга каракулей (для рендеринга PDF требуется pdfTeX ). В качестве альтернативы используйте scribble
исполняемый файл в файле.
#lang scribble/base
@; Generate a PDF or an HTML document using `scribble'
@(require (planet neil/numspell))
@title{99 Bottles of Beer}
In case you need some @emph{blah blah} in your life.
@(apply itemlist
(for/list ([n (in-range 99 0 -1)])
(define N (number->english n))
(define N-- (number->english (sub1 n)))
@item{@string-titlecase[N] bottles of beer on the wall,
@N bottles of beer.
Take one down, pass it around,
@N-- bottles of beer on the wall.}))
Самой поразительной особенностью языков Scribble является использование нового синтаксиса, разработанного специально для насыщенного текстом кода. [4] Синтаксис допускает текст в свободной форме, интерполяцию строк, настраиваемые кавычки и полезен в других приложениях, таких как предварительная обработка текста , генерация текста и системы шаблонов HTML. Обратите внимание, что синтаксис расширяет простые S-выражения и реализуется как альтернативный вход для таких выражений.
#lang scribble/text
Hi,
I'm a text file -- run me.
@(define (thrice . text) @list{@text, @text, @text})
@thrice{SPAM}!
@thrice{HAM}!
Типизированная ракетка [ править ]
Typed Racket — это статически типизированный вариант Racket. Реализуемая им система типов уникальна тем, что мотивацией при ее разработке было включение как можно большего количества идиоматического кода Racket — в результате она включает подтипы, объединения и многое другое. [5] Другая цель Typed Racket — обеспечить миграцию частей программы на типизированный язык, чтобы обеспечить возможность вызова типизированного кода из нетипизированного кода и наоборот, генерируя динамические контракты для обеспечения соблюдения инвариантов типов. [6] Это считается желательной особенностью этапов жизненного цикла приложения, поскольку оно развивается от «сценария» до «приложения», где статическая типизация помогает поддерживать большой объем кода.
#lang typed/racket
;; Using higher-order occurrence typing
(define-type Str-or-Num (U String Number))
(: tog ((Listof Str-or-Num) -> String))
(define (tog l)
(apply string-append (filter string? l)))
(tog (list 5 "hello " 1/2 "world" (sqrt -1)))
Ленивая ракетка [ править ]
The lazy
язык — это язык с семантикой ленивых вычислений , похожий на Haskell . В следующем примере fibs
представляет собой бесконечный список, 1000-й элемент которого не будет вычисляться до тех пор, пока его значение не потребуется для распечатки.
#lang lazy
;; An infinite list:
(define fibs
(list* 1 1 (map + fibs (cdr fibs))))
;; Print the 1000th Fibonacci number:
(print (list-ref fibs 1000))
Логическое программирование [ править ]
Racket поставляется с тремя языками логического программирования : Racklog, язык, похожий на Пролог ; реализация журнала данных ; и порт miniKanren . В отличие от синтаксиса Scribble, первые два из этих языков используют совершенно новый синтаксис, а не расширение S-выражений. Если его использовать в DrRacket, он обеспечивает правильную подсветку, обычный набор инструментов для проверки синтаксиса и REPL Prolog/Datalog.
#lang datalog
ancestor(A, B) :- parent(A, B).
ancestor(A, B) :-
parent(A, C), D = C, ancestor(D, B).
parent(john, douglas).
parent(bob, john).
ancestor(A, B)?
Инструменты образования [ править ]
Группа PLT, разрабатывающая Racket, традиционно занимается образованием на всех уровнях. Одной из самых ранних исследовательских идей, продвигаемых группой, является использование языковых уровней, которые ограничивают новых учащихся, одновременно предоставляя им полезные сообщения об ошибках, соответствующие уровню знаний учащегося. Этот подход широко используется в учебнике «Как проектировать программы» (HtDP), написанном несколькими разработчиками PLT, а также в проекте ProgramByDesign . Следующая программа использует htdp/bsl
— «язык начинающего студента». Он использует 2htdp/image
библиотека для создания изображений на языках обучения, а также 2htdp/universe
библиотека интерактивной анимации.
#lang htdp/bsl
;; Any key inflates the balloon
(require 2htdp/image)
(require 2htdp/universe)
(define (balloon b) (circle b "solid" "red"))
(define (blow-up b k) (+ b 5))
(define (deflate b) (max (- b 1) 1))
(big-bang 50 (on-key blow-up) (on-tick deflate)
(to-draw balloon 200 200))
ALGOL[editАЛГОЛ
Racket поставляется с полной реализацией языка ALGOL 60 .
#lang algol60
begin
integer procedure SIGMA(x, i, n);
value n;
integer x, i, n;
begin
integer sum;
sum := 0;
for i := 1 step 1 until n do
sum := sum + x;
SIGMA := sum;
end;
integer q;
printnln(SIGMA(q*2-1, q, 7));
end
Плай и плей-напечатанный [ править ]
#lang plai
#lang plai-typed
Другой поддерживаемый язык — plai, который, как и Racket, можно печатать или не печатать. «Модули, написанные на Plai, экспортируют каждое определение (в отличие от схемы)». [7] «Язык Typed PLAI отличается от традиционного Racket прежде всего тем, что он статически типизирован. Он также предоставляет некоторые полезные новые конструкции: определение типа, тип-регистр и тестирование». [8]
Создание языков [ править ]
Наконец, следующий пример представляет собой реализацию нового языка:
#lang racket
(provide (except-out (all-from-out racket)
#%top #%app)
(rename-out [top #%top] [app #%app]))
(define-syntax-rule (top . x) 'x)
(define-syntax-rule (app f . xs)
(if (hash? f) (hash-ref f . xs) (f . xs)))
Этот язык:
- обеспечивает все, начиная
racket
язык, так что это чем-то похожий вариант, - за исключением двух специальных «макросов-перехватчиков», реализующих несвязанный поиск переменных и вызовы функций, вместо них предоставляются новые формы для
- неявно указывать все неизвестные переменные
- разрешить использование хеш-таблиц в качестве функций, аргументы которых используются для поиска в хеш-таблице. [9]
Если этот код хранится в mylang.rkt
файл, его можно использовать следующим образом:
#lang s-exp "mylang.rkt" ; sexpr syntax, using mylang semantics
(define h (make-hasheq))
(hash-set! h A B) ; A and B are self-evaluating here
(h A) ; the hash table is used as a function
Ссылки [ править ]
- ^ Тобин-Хохштадт, С.; Сент-Амур, В.; Калпеппер, Р.; Флэтт, М.; Феллейзен, М. (2011). «Языки как библиотеки» (PDF) . Проектирование и реализация языков программирования .
- ^ Jump up to: Перейти обратно: а б ПЛАНЕТА : централизованная система распространения пакетов Racket.
- ^ «Восстановление графического слоя Racket» . 08.12.2010. Архивировано из оригинала 2 февраля 2013 г. Проверено 7 июля 2013 г.
- ^ Барзилай, Э. (2009). «Читатель каракулей» (PDF) . Схема и функциональное программирование .
- ^ Тобин-Хохштадт, С.; Феллайзен, М. (2008). «Проектирование и реализация типизированной схемы». Принципы языков программирования .
- ^ Тобин-Хохштадт, С.; Феллейзен, М. (2006). «Межъязыковая миграция: от сценариев к программам». Симпозиум по динамическим языкам .
- ^ «1 Схема PLAI» .
- ^ Кришнамурти, Шрирам. «Языки программирования: применение и интерпретация». Языки программирования: применение и интерпретация. Университет Брауна, Интернет. 14 марта 2016 г. < http://cs.brown.edu/courses/cs173/2012/book/ >.
- ^ Обратите внимание, что
#%app
— это макрос, который используется во всех вызовах функций, что делает этот язык не слишком эффективным, поскольку каждый вызов функции влечет за собой добавленное условие. Кроме того, макрос дважды вычисляет выражение функции, поэтому его не следует рассматривать как пример хорошего макропрограммирования.
- На момент редактирования в этой статье используется контент из «Racket» , который лицензируется таким образом, чтобы его можно было повторно использовать по лицензии Creative Commons Attribution-ShareAlike 3.0 Unported License , но не по GFDL . Все соответствующие условия должны быть соблюдены.