Программирование на языке Scala/Синтаксис параметров методов

Именованные аргументы править

Именованные аргументы позволяют однозначно указывать какой аргумент какому параметру предназначен. При этом порядок определения аргументов не важен.

Пример:

def add(param1: Int, param2: Int): Int = param1 + param2 // определена функция с двумя параметрами
val res = add(param2 = 1, param1 = 2) // вызов метода add с указанием именованных аргументов

В данном примере нарушен порядок указания аргументов, но это неважно, так как у аргументов указан их параметр.

Можно также смешивать позиционные и именованные аргументы. В этом случае сначала указываются позиционные аргументы.

Именованные аргументы, хотя и увеличивают размер кода при вызове методов, но очень сильно помогают в понимании кода и устранений ошибок при указании аргументов. Рекомендуется при списке параметров метода больше двух, использовать наименованные аргументы.

Необязательные параметры править

При определении метода, для параметров метода можно указать аргумент (значение параметра), который будет использован, если при вызове метода аргумент для параметра не будет задан. Такие аргументы называются аргументами по умолчанию (значениями по умолчанию). Соотвественно, при вызове метода, указание аргументов для параметров без аргументов по умолчанию - является обязательным, а для параметров с определенным аргументом по умолчанию - необязательным.

Пример:

def add(param1: Int, param2: Int = 1): Int = param1 + param2

здесь:

  • param1: Int - обязательный параметр с типом Int,
  • param2: Int = 1 - необязательный параметр с типом Int с аргументом по умолчанию 1.

Вызов метода:

val res = add(2, 2) // заданы все аргументы метода, результат 4
val res2 = add(2) // задан только обязательный аргумент, второй параметр использует аргумент по умолчанию 1, результат 3

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

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

Пример:

def add(param1: Int = 1, param2: Int = 2): Int = param1 + param2  
val res = add(2, 2) // неоднозначности нет, все агргументы распределены по параметрам.
val res = add(2) // Компилятором выявлена ошибка! Неоднозначность. Невозможно определить к какому параметру указаный аргумент.
val res = add(param2 = 2) // неоднозначности нет. Указанный агргумент привязат к параметру param2.

Не следует путать необязательный параметр и пропущенный параметр c заполнителем. Это совершенно разные действия с разным результатом.

def add(param1: Int, param2: Int = 2): Int = param1 + param2
val res: Int = add(2) // не указан второй параметр. Он необязательный, так как у него есть аргумент по умолчанию. Результат 4
val res2: Int => Int = add(2, _) // второй параметр пропущен. Результатом является новая функция с одним параметром типа Int => Int

Список аргументов переменной длины править

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

Пример:

def add(param1: Int, param2: Char*): String = param1.toString + param2.mkString  
val res = add(1, 'a', 'b') // Результат строка "1ab"
val res2 = add(1, 'a', 'b', 'c') // // Результат строка "1abc"
val res3 = add(1) // // Результат строка "1"

где Char* - к типу Char добавлена звездочка *, которая указывает, что параметр может принимать список аргументов типа Char. В теле метода с таким параметром нужно работать как со списком таких значений.

Работа со списками значений будет освещена в будущих занятиях. Однако приведем еще один специальный пример работы с повторяющимся параметром на будущее:

val seq = Seq('a', 'b')
val res4 = add(1, seq: _*) // '''seq: _*''' - такое специальное указание аргумента заставляет компилятор разрешить использование списка аргументов для повторяющегося параметра.

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

Группы параметров править

Методы могут объявляться с несколькими списками параметров (группы параметров). Каждый список параметров обрамлен круглыми скобками.

def multiAdd(param1: Int, param2: Int)(param3: Int, param4: Int): Int = (param1 + param2) * (param3 + param4)

где:

  • (param1: Int, param2: Int) - первый список параметров
  • (param3: Int, param4: Int) - второй список параметров

В теле метода отдельные списки параметров ни к чему не обязывают. Параметры также используются указывая их название. Количество списков параметров и количество параметров в списке - без ораничений.

Использование групп параметров править

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

def multiAdd(param1: Int, param2: Int)(param3: Int, param4: Int): Int = (param1 + param2) * (param3 + param4)

2. Группу параметров проще сделать пропущенной, чем каждый парамет в отдельности

def multiAdd(param1: Int, param2: Int)(param3: Int, param4: Int): Int = (param1 + param2) * (param3 + param4)
val res = multiAdd(1, 2)(_, _) // Сложнее, результат функция (Int, Int) => Int
val res2 = multiAdd(1, 2) _ // Проще, результат функция (Int, Int) => Int

3. Группу параметров из одного функционального параметра можно задать как функциональный блок выражений. При этом в методе присуствуют и другие параметры в другой группе:

def extraAdd(param1: Int, param2: Int)(param3: Int => Int): Int = param3(param1 + param2)

// Вызов метода extraAdd с указанием функционального блока для параметра param3
val res = extraAdd(1, 2){ param =>
  param * 2 
}

4. Можно использовать параметр из предыдущей группы для вычисления аргумента по умолчанию:

def g(x: Int)(y: Int = x * 2) = x + y

5. Можно иметь несколько параметров переменной длины

def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum

6. Можно задавать параметры разных видов в разных группах:

def gaga(x: Int)(implicit y: Int) = ???

поскольку модификатор implicit является модификатором для всего списка аргументов. Более подробно об модификаторе implicit будет освещено в последующих уроках.

7. Группы аргументов в некоторых сложных случаях единственный способ сделать вывод типов:

def foo[T](a: T, b: T)(op: (T,T) => T) = op(a, b)
foo(1, 2){_ + _}   // compiler can infer the type of the op function

Разбор этого сложного примера отложем на будущее.

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

Преобразование метода в анонимную функцию править

Конвертировать метод в ананимную функцию можно двумя способами:

val fun: (Int, Int) => Int = add  // первый способ. Указание типа при определении
val fun2 = add _  // второй способ. Пропуск списка параметров.

В некоторых случаях преобразование происходит автоматически.

Домашнее задание править

1. В учебном проекте создайте файл Methods.scala.

2. Есть метод

def method(param: Char): Char = param.toUpper

"a".map(???)

Как методу map передать в качестве аргумента метод method?

3. Есть два метода

def method(param: Char): Char = param.toUpper
def method2(param: Char): Char = param.toLower

val tuple = (???, ???)

Как сделать кортеж с этими двумя методами?

4. Есть метод с двумя группами параметров

def method3(param1: Char, param2: Int)(funParam: (Char, Char) => String): String = funParam(param1, param2.toChar)

Вызовите метод первый раз, указав аргументом параметра funParam - произвольную анонимную функцию.

Вызовите метод второй раз, указав аргументом параметра funParam - произвольный многострочный функциональный блок.

5. Есть метод с одной группой параметров

def method4(param1: Char, param2: Int, param3: (Char, Int)): String = 
  param1.toString + param2.toString + param3._1.toString + param3._2.toString

Переделайте сигнатуру метода method4 таким образом, чтобы param2 был необязательным, и имел аргумент по умолчанию в виде param1.toChar. Также, чтобы param3 тоже был необязательным, и имел аргумент по умолчанию (param1, param2)

Результат метода method4 не должен измениться после внесения изменений в сигнатуру.

6. Есть метод с повторяющимся параметром:

def add(param1: Int, param2: Int*): Int = ???

Задайте в теле метода выражение, которое подсчитает сумму всех получаемых аргументов. Для получения суммы параметра param2 посмотрите доступные методы у данного параметра.

Проверьте:

add(1, 1) // Результат 2
add(1, 1, 1) // Результат 3

7. Есть метод с большим списком параметров:

def add(param1: Int, param2: Int, param3: Int, param4: Int, param5: Int, param6: Int): Int = 
  param1 + param2 * param3 / param4 - param5 + param6

Этот метод нужно вызвать со следующий списком аргументов:

param2 = 3, param3 = 4, param5 = 6, param6 = 7, param1 = 2, param4 = 5

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

8. Есть метод

def add(param1: Int, param2: Int = 1, param3: Int = 2): Int = 
  param1 + param2 + param3

Вызовите метод указав только два аргумента param1 и param3.

Ответы на домашнее заданее править