Локальные объявления

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

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются https://github.com/hexlet-basics
Если вы столкнулись с трудностями и не знаете, что делать, задайте вопрос в нашем большом и дружном cообществе
Упражнение доступно только авторизованным пользователям.

Пожалуйста, авторизуйтесь, это необходимо для отслеживания прогресса выполнения уроков. Если у вас ещё нет учётной записи, то сейчас самое время создать аккаунт.