Racket: Локальные объявления
define в Racket может использоваться как на уровне модуля, так и внутри функций:
(define (f)
(define text "lorem")
(displayln text))
(f) ; => "lorem"
; у `define text` локальная область видимости
(displayln text) ; <error>Но с ним связано несколько тонких моментов:
- Хотя лиспы похожи между собой, конкретно
defineведет себя совершенно по-разному в разных диалектах Lisp. В некоторых объявления останутся локальными для текущей области видимости, в других же объявление всегда будет глобальным. - Объявления переменных должны идти в самом начале функции, до любых других выражений.
Существует и другой способ объявить локальные переменные, гораздо более популярный и предсказуемый:
(let ([text "lorem"]) (displayln text)) ; => loremКаждое объявление в let - это список из имени и выражения, вычисленное значение которого будет ассоциировано с именем. В наших примерах такие объявления записываются в квадратных скобках исключительно для удобства. Интерпретатору практически всегда понятно, что мы имеем в виду, вне зависимости от того, какие скобки мы использовали (Да-да, в Racket практически везде можно использовать квадратные скобки вместо круглых!). Перепишем уже знакомую нам функцию sum, используя локальные объявления:
; форма записи без локальных объявлений:
(define sum (lambda (x y) (+ x y)))
(sum 8 7) ; вызов глобальной функции
; форма записи с локальными объявлениями:
(let ([sum (lambda (x y) (+ x y))])
(sum 8 7)) ; вызов локальной функцииВсе объявления внутри let доступны только в выражениях, которые вызываются внутри самого let после списка объявлений (Объявления не видят друг друга! Подробнее - ниже). Вот ещё несколько более сложных примеров, с несколькими объявлениями:
(let ([x 2]
[y (+ 4 3)])
(+ x y)) ; 9(define (sum-of-squares x y)
(let ([x-square (* x x)]
[y-square (* y y)])
(+ x-square y-square)))Можно пойти еще дальше - вызывать локальную функцию внутри глобальной:
(define (sum-of-squares x y)
(let ([square (lambda (n) (* n n))])
(+ (square x) (square y))))
(sum-of-squares 8 7) ; => 113Объявления, созданные в рамках одного использования let, не видны друг другу. Поэтому мы не можем сделать так:
(let ([x 2]
[y (* x 10)]) ; здесь - ошибка, потому что
; переменная x ещё не объявлена.
(+ x y))Форма let так устроена, что каждое объявление из списка она создаёт "с чистого листа", поэтому объявления не зависят друг от друга, что иногда удобно. Скажем, мы можем свободно менять местами объявления в пределах одного списка. Если же нам непременно нужно использовать в последующих объявлениях предыдущие, мы можем воспользоваться формой let*:
(let* ([x 2]
[y (* x 20)]
[z (+ x y)])
z) ; 42Задание
Реализуйте функцию square-of-sum, которая сначала складывает числа, а затем возводит в квадрат. Воспользуйтесь локальными объявлениями для хранения промежуточных результатов вычисления.
(square-of-sum 2 3) ; 25Racket: Локальные объявления
define в Racket может использоваться как на уровне модуля, так и внутри функций:
(define (f)
(define text "lorem")
(displayln text))
(f) ; => "lorem"
; у `define text` локальная область видимости
(displayln text) ; <error>Но с ним связано несколько тонких моментов:
- Хотя лиспы похожи между собой, конкретно
defineведет себя совершенно по-разному в разных диалектах Lisp. В некоторых объявления останутся локальными для текущей области видимости, в других же объявление всегда будет глобальным. - Объявления переменных должны идти в самом начале функции, до любых других выражений.
Существует и другой способ объявить локальные переменные, гораздо более популярный и предсказуемый:
(let ([text "lorem"]) (displayln text)) ; => loremКаждое объявление в let - это список из имени и выражения, вычисленное значение которого будет ассоциировано с именем. В наших примерах такие объявления записываются в квадратных скобках исключительно для удобства. Интерпретатору практически всегда понятно, что мы имеем в виду, вне зависимости от того, какие скобки мы использовали (Да-да, в Racket практически везде можно использовать квадратные скобки вместо круглых!). Перепишем уже знакомую нам функцию sum, используя локальные объявления:
; форма записи без локальных объявлений:
(define sum (lambda (x y) (+ x y)))
(sum 8 7) ; вызов глобальной функции
; форма записи с локальными объявлениями:
(let ([sum (lambda (x y) (+ x y))])
(sum 8 7)) ; вызов локальной функцииВсе объявления внутри let доступны только в выражениях, которые вызываются внутри самого let после списка объявлений (Объявления не видят друг друга! Подробнее - ниже). Вот ещё несколько более сложных примеров, с несколькими объявлениями:
(let ([x 2]
[y (+ 4 3)])
(+ x y)) ; 9(define (sum-of-squares x y)
(let ([x-square (* x x)]
[y-square (* y y)])
(+ x-square y-square)))Можно пойти еще дальше - вызывать локальную функцию внутри глобальной:
(define (sum-of-squares x y)
(let ([square (lambda (n) (* n n))])
(+ (square x) (square y))))
(sum-of-squares 8 7) ; => 113Объявления, созданные в рамках одного использования let, не видны друг другу. Поэтому мы не можем сделать так:
(let ([x 2]
[y (* x 10)]) ; здесь - ошибка, потому что
; переменная x ещё не объявлена.
(+ x y))Форма let так устроена, что каждое объявление из списка она создаёт "с чистого листа", поэтому объявления не зависят друг от друга, что иногда удобно. Скажем, мы можем свободно менять местами объявления в пределах одного списка. Если же нам непременно нужно использовать в последующих объявлениях предыдущие, мы можем воспользоваться формой let*:
(let* ([x 2]
[y (* x 20)]
[z (+ x y)])
z) ; 42Задание
Реализуйте функцию square-of-sum, которая сначала складывает числа, а затем возводит в квадрат. Воспользуйтесь локальными объявлениями для хранения промежуточных результатов вычисления.
(square-of-sum 2 3) ; 25Ваше упражнение проверяется по этим тестам
#lang racket
(require (only-in rackunit check-equal? test-begin))
(require "index.rkt")
(test-begin
(check-equal? (square-of-sum 2 3) 25)
(check-equal? (square-of-sum 5 8) 169))Решение учителя откроется через:
20:00
