[Scheme ]

Функции с переменным количеством аргументов

Мы уже рассмотрели как создавать свои функции, теперь мы рассмотрим процесс создания функций с переменным количеством аргументов.

Уже знакомая вам функция "area-of-disk" принимает один аргумент, если мы попытаемся вызвать её с другим количеством аргументов то мы получим ошибку:

(define (area-of-disk r) (* pi (* r r)))

->/dev/stdin:3:1: call to 'area-of-disk' has too few arguments (0; must be 1)

Давайте определим функцию для вычисления площади прямоугольника, если при вызове функции задаётся всего один аргумент, то возвращается площадь квадрата, а если два, то прямоугольника:

(define area-of-rect
 (case-lambda
  ((w) (* w w))
  ((w h) (* w h ))
 )
)
(display "вызов функции с одним аргументом:")
(display (area-of-rect 10))
(newline)
(display "вызов функции с двумя аргументами:")
(display (area-of-rect 10 20))
(newline)

Как вы видите, здесь мы используем специальную конструкцию "case-lambda":

(case-lambda
 (
  (w);--первый набор аргументов
  (* w w);--код, который должен исполняться при одном аргументе
 )
 (
  (w h);--второй набор аргументов
  (* w h );--код, который должен исполняться при двух аргументах
 )
)

Давайте теперь напишем новый вариант функции "substring", который моожет принимать два или три параметра, если функция вызывается с тремя аргументами, то она должна работать как стандартная функция "substring", а если с двумя аргументами, то она должна возвращать подстроку представляющую из себя как бы "хвост" исходной строки, начинающийся с указанного в виде параметра индекса.

(define my-substring
 (case-lambda
  ((str start) (substring str start (string-length str)))
  ((str start end) (substring str start end))
 )
)

Количество аргументов может быть достаточно велико и на момент создания функции может быть не известно точное количество аргументов, которые будут передаваться этой функции. В таких случаях можно использовать следующий синтаксис:

(define (arg1 arg2 ... arg3 . argList)
   expr1 expr2 ...)
   
Давайте определим две функции использующие этот синтаксис и посмотрим как они работают:

(define (test0 . aList)
 (display aList)
 (newline))
(define (test1 a1 . aList)
 (display aList)
 (newline))
(define (test2 a1 a2 . aList)
 (display aList)
 (newline))
(define (test3 a1 a2 a3 . aList)
 (display aList)
 (newline))

(test0 1 2 3 4 5)
(test1 1 2 3 4 5)
(test2 1 2 3 4 5)
(test3 1 2 3 4 5)

->(1 2 3 4 5)
->(2 3 4 5)
->(3 4 5)
->(4 5)

Как мы видим, в переменную aList помещаются те аргументы на которые не хватило обычных ... даже не знаю как их назвать, но вы, мои юные падаваны, и так всё прекрасно понимаете, что, куда и почему помещается, да?

Функции как аргументы

В отличие от некоторых других менее сознательных языков программирования, в Scheme функции можно совершенно безнаказанно передавать в качестве аргументов в функции и сохранять их в различных переменных.

(define (op f o1 o2) (f o1 o2))


(op + 4 2)

->6

(op - 4 2)

->2

(op * 4 2)

->8

(op / 4 2)

->2

(op max 4 2)

->4

Возврат функции из другой функции

А теперь ещё один пример, в данном примере одна функция будет возвращаться из другой функции.

(define (get-function funcName)
 (cond
  ((eq? funcName "plus") +)
  ((eq? funcName "minus") -)
  ((eq? funcName "mult") *)
  ((eq? funcName "div") /)
 )
)

(display ((get-function "plus") 4 2))
(newline)
(display ((get-function "minus") 4 2))
(newline)
(display ((get-function "mult") 4 2))
(newline)
(display ((get-function "div") 4 2))
(newline)


->6
->2
->8
->2