Значення, що повертаються

Значення, що повертаються, дозволяють керувати потоком без додаткового стану. У Scheme останній обчислений вираз стає результатом функції.

На цій сторінці помічники перевірки з прикладу обміну повідомленнями показують, як явні повернення спрощують код.

Що таке значення, що повертається?

У Scheme результат функції визначає останній обчислений вираз. Якщо явного повернення немає, функція дає #f (хибність) або undefined.

Повернемося до (is-valid-string?):

;; Призначення: перевірити, що message — непорожній рядок
(define (is-valid-string? message)
  ;; Перевірити, що message — непорожній рядок
  (if (or (not (string? message)) (string=? message ""))
    (error "Message must be a non-empty string")))

Якщо повідомлення недійсне — error. Якщо дійсне — явного результату немає, і функція повертає #f за замовчуванням.

Явні значення, що повертаються

Можна зробити результат явним — наприклад, повернути #t (істина), коли перевірка пройдена:

;; Призначення: перевірити, що output — допустимий пункт призначення
(define (is-valid-output-display? output)
  ;; Перевірити, що output — один із очікуваних виходів
  (if (not (member output '(dialog-box status-bar error-console terminal)))
    (error "Invalid output destination: " output)
    #t))

Тут функція повертає #t при успішній перевірці — зручніше використовувати її там, де потрібен логічний результат.

Ефективне використання повернень

Явні #t, #f або конкретні результати роблять функції передбачуванішими. Повернене значення можна використати в коді, що викликає, або передати далі.

Приклад керування потоком:

;; Призначення: направити повідомлення на відповідний вихід
(define (send-message message output)
  (if (is-valid-output-display? output)
    (cond
      ((eq? output 'error-console) (send-to-error-console message))
      ((eq? output 'dialog-box) (send-to-dialog-box message))
      ((eq? output 'status-bar) (send-to-status-bar message))
      ((eq? output 'terminal) (send-to-terminal message)))))

send-message покладається на результат is-valid-output-display?. Якщо перша перевірка не пройде, cond не виконається. Код читається природно: «якщо вихід допустимий — надіслати».

Логіка if у Scheme

Короткий огляд умовної логіки. Scheme використовує if для вибору між двома шляхами.

(if (conditional test)
  do-if-true
  do-if-false)

Якщо умова істинна — перша гілка; якщо хибна — друга.

Для кількох дій у гілці використовуйте begin:

(if (conditional test)
  (begin
    do-if-true)
  (begin
    do-if-false))

Ось бібліотека з явними поверненнями, що керують виконанням:

Рефакторинг із поверненнями

;; Призначення: надіслати в рядок стану; повертає #t при успіху
(define (send-to-status-bar message)
  (if (is-valid-string? message)
    (begin
      (lumi-message-set-handler MESSAGE-BOX)
      (lumi-message message)
      (lumi-message-set-handler ERROR-CONSOLE)
      #t)
    #f))

;; Призначення: надіслати в діалогове вікно; повертає #t при успіху
(define (send-to-dialog-box message)
  (if (is-valid-string? message)
    (begin
      (lumi-message-set-handler MESSAGE-BOX)
      (lumi-message (string-append message "\n"))
      (lumi-message-set-handler ERROR-CONSOLE)
      #t)
    #f))

;; Призначення: надіслати в консоль помилок; повертає #t при успіху
(define (send-to-error-console message)
  (if (is-valid-string? message)
    (begin
      (lumi-message-set-handler ERROR-CONSOLE)
      (lumi-message message)
      #t)
    #f))

;; Призначення: надіслати в термінал; повертає #t при успіху
(define (send-to-terminal message)
  (if (is-valid-string? message)
    (begin
      (display message)
      (lumi-message-set-handler ERROR-CONSOLE)
      #t)
    #f))

;; Призначення: маршрутизація; повертає #t при успіху
(define (send-message message output)
  (if (is-valid-string-output? output)
    (cond
      ((eq? output 'error-console) (send-to-error-console message))
      ((eq? output 'dialog-box) (send-to-dialog-box message))
      ((eq? output 'status-bar) (send-to-status-bar message))
      ((eq? output 'terminal) (send-to-terminal message)))
    #f))

;; Призначення: перевірити непорожній рядок; повертає #t якщо валідно
(define (is-valid-string? message)
  (if (or (not (string? message)) (string=? message ""))
    (begin
      (error "Message must be a non-empty string")
      #f)
    #t))

;; Призначення: перевірити допустимий output; повертає #t якщо валідно
(define (is-valid-string-output? output)
  (if (not (member output '(dialog-box status-bar error-console terminal)))
    (begin
      (error "Invalid output destination: " output)
      #f)
    #t))

Висновок

Значення, що повертаються, роблять функції гнучкими та придатними для повторного використання. Свідомий вибір того, що повертає кожна функція, покращує взаємодію між частинами коду. #t, #f або конкретні результати дають контроль над потоком програми.