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

define в Racket может использоваться как на уровне модуля, так и внутри функций:

(define (f)
  (define text "lorem")
  (displayln text))

(f) ; => "lorem"
; у define локальная область видимости
(displayln text) ; error

Но с ним связано несколько тонких моментов:

  • Хотя лиспы похожи между собой, конкретно define ведет себя совершенно по-разному в разных диалектах Lisp. В некоторых объявления останутся локальными для текущей области видимости, в других же объявление всегда будет глобальным.
  • Объявления переменных должно идти в самом начале функции, до любых других выражений.

Существует и другой способ объявить локальные переменные, гораздо более популярный и предсказуемый:

; создается локальное объявление x и затем его значение умножается на два
(let ([x 5]) (* x 2))

Все объявления внутри let доступны только в выражениях, которые вызываются внутри самого let после списка объявлений (объявления не видят друг друга! Подробнее - ниже). Вот более сложный пример, с несколькими объявлениями:

(let ([x 2]
      [y (+ 4 3)])
  (+ x y)) ; 9

Каждое объявление в let - это список из имени и выражения, вычисленное значение которого, будет ассоциировано с именем. В наших примерах такие объявления записываются в квадратных скобках исключительно для вашего удобства - интерпретатору практически всегда понятно, что мы имеем в виду, вне зависимости от того, какие скобки мы использовали (да-да, в Racket практически везде можно использовать квадратные скобки вместо круглых!). Напишем функцию, которая считает сумму квадратов двух чисел, используя локальные объявления:

(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))))

Объявления, созданные в рамках одного использования 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

Упражнение доступно только авторизованным пользователям.

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