Особенности методов | Scala 3 — Book
Info: JavaScript is currently disabled, code tabs will still work, but preferences will not be remembered.
В этом разделе представлены различные аспекты определения и вызова методов в Scala 3.
Определение методов
В Scala методы обладают множеством особенностей, в том числе:
- Generic (типовые) параметры
- Значения параметров по умолчанию
- Несколько групп параметров
- Контекстные параметры
- Параметры по имени
- и другие…
Некоторые из этих функций демонстрируются в этом разделе, но когда вы определяете “простой” метод, который не использует эти функции, синтаксис выглядит следующим образом:
def methodName(param1: Type1, param2: Type2): ReturnType = { // тело метода // находится здесь }
def methodName(param1: Type1, param2: Type2): ReturnType = // тело метода // находится здесь end methodName // опционально
В этом синтаксисе:
- ключевое слово
def
используется для определения метода - для наименования методов согласно стандартам Scala используется camel case convention
- у параметров метода необходимо всегда указывать тип
- возвращаемый тип метода указывать необязательно
- методы могут состоять как только из одной строки, так и из нескольких строк
- метку окончания метода
end methodName
указывать необязательно, её рекомендуется указывать только для длинных методов
Вот два примера однострочного метода с именем add
, который принимает два входных параметра Int
. Первая версия явно показывает возвращаемый тип метода — Int
, а вторая — нет:
def add(a: Int, b: Int): Int = a + b def add(a: Int, b: Int) = a + b
У публичных методов рекомендуется всегда указывать тип возвращаемого значения. Объявление возвращаемого типа может упростить его понимание при просмотре кода другого человека или своего кода спустя некоторое время.
Вызов методов
Вызов методов прост:
val x = add(1, 2) // 3
Коллекции Scala имеют десятки встроенных методов. Эти примеры показывают, как их вызывать:
val x = List(1, 2, 3) x.size // 3 x.contains(1) // true x.map(_ * 10) // List(10, 20, 30)
Внимание:
size
не принимает аргументов и возвращает количество элементов в списке- метод
contains
принимает один аргумент — значение для поиска map
принимает один аргумент — функцию; в данном случае в него передается анонимная функция
Многострочные методы
Если метод длиннее одной строки, начинайте тело метода со второй строки с отступом вправо:
def addThenDouble(a: Int, b: Int): Int = { // представим, что это тело метода требует несколько строк val sum = a + b sum * 2 }
def addThenDouble(a: Int, b: Int): Int = // представим, что это тело метода требует несколько строк val sum = a + b sum * 2
В этом методе:
sum
— неизменяемая локальная переменная; к ней нельзя получить доступ вне метода- последняя строка удваивает значение
sum
— именно это значение возвращается из метода
Когда вы вставите этот код в REPL, то увидите, что он работает как требовалось:
scala> addThenDouble(1, 1) res0: Int = 4
Обратите внимание, что нет необходимости в операторе return
в конце метода. Поскольку почти все в Scala является выражением — то это означает,
что каждая строка кода возвращает (или вычисляет) значение — нет необходимости использовать return
.
Это видно на примере того же метода, но в более сжатой форме:
def addThenDouble(a: Int, b: Int): Int = (a + b) * 2
В теле метода можно использовать все возможности Scala:
if
/else
выраженияmatch
выражения- циклы
while
- циклы
for
иfor
выражения - присвоение переменных
- вызовы других методов
- определения других методов
В качестве ещё одного примера многострочного метода, getStackTraceAsString
преобразует свой входной параметр Throwable
в правильно отформатированную строку:
def getStackTraceAsString(t: Throwable): String = { val sw = new StringWriter() t.printStackTrace(new PrintWriter(sw)) sw. toString }
def getStackTraceAsString(t: Throwable): String = val sw = StringWriter() t.printStackTrace(PrintWriter(sw)) sw.toString
В этом методе:
- в первой строке переменная
sw
принимает значение нового экземпляраStringWriter
- вторая строка сохраняет содержимое трассировки стека в
StringWriter
- третья строка возвращает строковое представление трассировки стека
Значения параметров по умолчанию
Параметры метода могут иметь значения по умолчанию.
В этом примере для параметров timeout
и protocol
заданы значения по умолчанию:
def makeConnection(timeout: Int = 5_000, protocol: String = "http") = { println(f"timeout = ${timeout}%d, protocol = ${protocol}%s") // здесь ещё какой-то код ... }
def makeConnection(timeout: Int = 5_000, protocol: String = "http") = println(f"timeout = ${timeout}%d, protocol = ${protocol}%s") // здесь ещё какой-то код . ..
Поскольку параметры имеют значения по умолчанию, метод можно вызвать следующими способами:
makeConnection() // timeout = 5000, protocol = http makeConnection(2_000) // timeout = 2000, protocol = http makeConnection(3_000, "https") // timeout = 3000, protocol = https
Вот несколько ключевых моментов об этих примерах:
- В первом примере аргументы не предоставляются, поэтому метод использует значения параметров по умолчанию:
5_000
иhttp
- Во втором примере для параметра
timeout
указывается значение2_000
, поэтому оно используется вместе со значением по умолчанию дляprotocol
- В третьем примере значения указаны для обоих параметров, поэтому используются они.
Обратите внимание, что при использовании значений параметров по умолчанию потребителю кажется, что он может работать с тремя разными переопределенными методами.
Именованные параметры
При желании вы также можете использовать имена параметров метода при его вызове.
Например, makeConnection
может также вызываться следующими способами:
makeConnection(timeout=10_000) makeConnection(protocol="https") makeConnection(timeout=10_000, protocol="https") makeConnection(protocol="https", timeout=10_000)
В некоторых фреймворках именованные параметры используются постоянно. Они также очень полезны, когда несколько параметров метода имеют один и тот же тип:
engage(true, true, true, false)
Без помощи IDE этот код может быть трудночитаемым, но так он становится намного понятнее и очевиднее:
engage( speedIsSet = true, directionIsSet = true, picardSaidMakeItSo = true, turnedOffParkingBrake = false )
Рекомендации о методах, которые не принимают параметров
Когда метод не принимает параметров, говорят, что он имеет arity уровень 0 (arity-0). Аналогично, если метод принимает один параметр — это метод с arity-1.Когда создаются методы arity-0:
- если метод выполняет побочные эффекты, такие как вызов
println
, метод объявляется с пустыми скобками. - если метод не выполняет побочных эффектов, например, получение размера коллекции, что аналогично доступу к полю в коллекции, круглые скобки опускаются.
Например, этот метод выполняет побочный эффект, поэтому он объявлен с пустыми скобками:
def speak() = println("hi")
При вызове метода нужно обязательно указывать круглые скобки, если он был объявлен с ними:
speak // ошибка: "method speak must be called with () argument" speak() // печатает "hi"
Хотя это всего лишь соглашение, его соблюдение значительно улучшает читаемость кода: с первого взгляда становится понятно, что метод с arity-0 имеет побочные эффекты.
Использование
if
в качестве тела методаПоскольку выражения if
/else
возвращают значение, их можно использовать в качестве тела метода. Вот метод с именем isTruthy
, реализующий Perl-определения true
и false
:
def isTruthy(a: Any) = { if (a == 0 || a == "" || a == false) false else true }
def isTruthy(a: Any) = if a == 0 || a == "" || a == false then false else true
Примеры показывают, как работает метод:
isTruthy(0) // false isTruthy("") // false isTruthy("hi") // true isTruthy(1.0) // true
Использование
match
в качестве тела метода Довольно часто в качестве тела метода используются match
-выражения.
Вот еще одна версия isTruthy
, написанная с match
выражением:def isTruthy(a: Any) = a match { case 0 | "" | false => false case _ => true }
def isTruthy(a: Matchable) = a match case 0 | "" | false => false case _ => true
Этот метод работает точно так же, как и предыдущий, в котором использовалось выражение
if
/else
. ВместоAny
в качестве типа параметра используетсяMatchable
, чтобы принять любое значение, поддерживающее сопоставление с образцом (pattern matching).
См. дополнительную информацию о trait
Matchable
в Справочной документации.
Контроль видимости методов в классах
В классах, объектах, trait-ах и enum-ах методы Scala по умолчанию общедоступны,
поэтому созданный здесь экземпляр
может получить доступ к методу speak
:
class Dog { def speak() = println("Woof") } val d = new Dog d.speak() // печатает "Woof"
class Dog: def speak() = println("Woof") val d = new Dog d.speak() // печатает "Woof"
Также методы можно помечать как private
.
Это делает их закрытыми в текущем классе, поэтому их нельзя вызвать или переопределить в подклассах:
class Animal { private def breathe() = println("I’m breathing") } class Cat extends Animal { // этот метод не скомпилируется override def breathe() = println("Yo, I’m totally breathing") }
class Animal: private def breathe() = println("I’m breathing") class Cat extends Animal: // этот метод не скомпилируется override def breathe() = println("Yo, I’m totally breathing")
Если необходимо сделать метод закрытым в текущем классе, но разрешить подклассам вызывать или переопределять его,
метод помечается как protected
, как показано в примере с методом speak
:
class Animal { private def breathe() = println("I’m breathing") def walk() = { breathe() println("I’m walking") } protected def speak() = println("Hello?") } class Cat extends Animal { override def speak() = println("Meow") } val cat = new Cat cat. walk() cat.speak() cat.breathe() // не скомпилируется, потому что private
class Animal: private def breathe() = println("I’m breathing") def walk() = breathe() println("I’m walking") protected def speak() = println("Hello?") class Cat extends Animal: override def speak() = println("Meow") val cat = new Cat cat.walk() cat.speak() cat.breathe() // не скомпилируется, потому что private
Настройка protected
означает:
- к методу (или полю) могут обращаться другие экземпляры того же класса
- метод (или поле) не виден в текущем пакете
- он доступен для подклассов
Методы в объектах
Ранее было показано, что trait-ы и классы могут иметь методы.
Ключевое слово object
используется для создания одноэлементного класса, и объект также может содержать методы.
Это хороший способ сгруппировать набор “служебных” методов.
Например, этот объект содержит набор методов, которые работают со строками:
object StringUtils { /** * Returns a string that is the same as the input string, but * truncated to the specified length. */ def truncate(s: String, length: Int): String = s.take(length) /** * Returns true if the string contains only letters and numbers. */ def lettersAndNumbersOnly_?(s: String): Boolean = s.matches("[a-zA-Z0-9]+") /** * Returns true if the given string contains any whitespace * at all. Assumes that `s` is not null. */ def containsWhitespace(s: String): Boolean = s.matches(".*\\s.*") }
object StringUtils: /** * Returns a string that is the same as the input string, but * truncated to the specified length. */ def truncate(s: String, length: Int): String = s.take(length) /** * Returns true if the string contains only letters and numbers. */ def lettersAndNumbersOnly_?(s: String): Boolean = s.matches("[a-zA-Z0-9]+") /** * Returns true if the given string contains any whitespace * at all. Assumes that `s` is not null. */ def containsWhitespace(s: String): Boolean = s.matches(".*\\s.*") end StringUtils
Методы расширения
Есть много ситуаций, когда необходимо добавить функциональность к закрытым классам. Например, представьте, что у вас есть класс Circle
, но вы не можете изменить его исходный код.
Это может быть определено в сторонней библиотеке так:
case class Circle(x: Double, y: Double, radius: Double)
Если вы хотите добавить методы в этот класс, то можете определить их как методы расширения, например:
implicit class CircleOps(c: Circle) { def circumference: Double = c.radius * math.Pi * 2 def diameter: Double = c.radius * 2 def area: Double = math.Pi * c.radius * c.radius }
В Scala 2 используйте implicit class
, подробности здесь.
extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2 def diameter: Double = c.radius * 2 def area: Double = math.Pi * c.radius * c.radius
В Scala 3 используйте новую конструкцию extension
.
Дополнительные сведения см. в главах этой книги или в справочнике по Scala 3.
Теперь, когда у вас есть экземпляр Circle
с именем aCircle
,
то вы можете вызывать эти методы следующим образом:
aCircle. circumference aCircle.diameter aCircle.area
Дальнейшее изучение
Есть много чего, что можно узнать о методах, в том числе:
- Вызов методов в суперклассах
- Определение и использование параметров по имени
- Написание метода, который принимает параметр функции
- Создание встроенных методов
- Обработка исключений
- Использование входных параметров vararg
- Написание методов с несколькими группами параметров (частично применяемые функции).
- Создание методов с параметрами универсального типа.
Дополнительные сведения об этих функциях см. в других главах этой книги.
Метод Дельфи: оценка эффективности решений
Содержание статьи
Сегодня существует множество методов для принятия решений, но метод Дельфи считается одним из самых эффективных. Этот метод является универсальным, любой человек или группа людей может использовать его для исследований. Он подходит для решений задач как в работе, так и в личной жизни. В этой статье мы подробно рассмотрим сущность метода Дельфи и основные этапы проведения данного метода.
Основная суть метода Дельфи
Метод Дельфи заключается в экспертной оценке группы людей, но эта оценка должна проходить анонимно, заочно и включать в себя несколько уровней. Процесс может состоять из мозгового штурма, опросов и интервью. Группа людей должна формироваться из независимых экспертов, которые не знакомы друг с другом. Тем самым они не будут влиять на мнение друг друга, а исследование получится максимально чистым. К тому же участникам не обязательно находиться в одном помещении или даже в одном городе. Основная идея состоит в том, чтобы получить максимально объективную и надежную оценку решения проблемы при условии корректной обработки результатов.
В проведении метода Дельфи обязательно должны участвовать две группы:
- 1 группа — это независимые эксперты, которые оценивают проблему;
- 2 группа — это аналитики, которые обрабатывают оценки и приводят результаты к единому выводу.
Основные этапы метода Дельфи
Всего метод Дельфи включает в себя 3 этапа: подготовительный, основной и итоговый. Рассмотрим их подробнее.
Подготовительный этап
Подготовка к исследованию начинается с подбора экспертов в группу. Рекомендуется собирать группу из 20 человек, но можно сформировать экспертную группу с меньшим количеством. Также важно четко сформулировать проблему, чтобы донести основной смысл до участников.
Основной этап
Озвучивание проблемы. Эксперты в начале должны разбить эту проблему на несколько вопросов. Затем аналитики отбирают из этих мелких вопросов самые популярные и из них кратко составляют опросник.
Отправка опросника. После того, как аналитики составили новый опросник, они отправляют его опять участникам. Эксперты еще раз просматривают вопросы, при необходимости дорабатывают их. Проверяют, достаточно ли информации получится в результате, нужно ли еще что-то добавить.
Ответы на новый опросник. Теперь каждый эксперт должен дать развернутый ответ на каждый вопрос в анкете. Придумать свой уникальный способ решения проблемы, прогнозировать риски и предположить возможные варианты развития, недостатки, а также прокомментировать ответы других участников. Задача аналитиков — отобрать похожие мнения и соединить их в один тезис. Если есть мнения, противоречащие друг другу, то они выносятся на обсуждения с экспертами. После чего, кто-то из участников может изменить свое мнение.
Подведение итогов. Процедура с опросником повторяется до тех пор, пока все участники не придут к единому выводу. Если же есть расхождения во мнениях, то, скорее всего, это указывает на тонкости проблемы, которые до этого не были проработаны.
Казалось бы, что в этом методе предусмотрено практически все, но это не так. Есть в методе Дельфи и весомые недостатки.
Недостатки метода Дельфи
- Как правило, аналитики сразу же отбрасывают нестандартные решения, которые могут быть самыми удачными.
- Общественное мнение не всегда является верным.
- Слишком много времени затрачивается на организацию опроса.
- Организаторы имеют слишком большое влияние на участников.
Метод Дельфи относится к самым популярным методам для стратегического планирования и управления, который применяют в Европе. Этот метод отлично подходит для крупных исследований, позволяет в целом оценить проблему и получить экспертное мнение. В жизни часто приходится планировать решения проблем самостоятельно в условиях ограниченного времени. Пройдите прямо сейчас тест «Планирование решения задач», чтобы оценить свой навык и при необходимости усовершенствовать его.
Выбор функций в машинном обучении [издание 2023]
ПредыдущийСледующий
Учебный плейлист
Входные переменные, которые мы даем нашим моделям машинного обучения, называются функциями. Каждый столбец в нашем наборе данных представляет собой функцию. Чтобы обучить оптимальную модель, нам нужно убедиться, что мы используем только основные функции. Если у нас слишком много функций, модель может фиксировать неважные закономерности и учиться на шуме. Метод выбора важных параметров наших данных называется Feature Selection.
В этой статье под названием «Все, что вам нужно знать о выборе функций» мы научим вас всему, что вам нужно знать о выборе функций.
Почему выбор функции?
Модели машинного обучения следуют простому правилу: что входит, то и выходит. Если мы поместим мусор в нашу модель, мы можем ожидать, что вывод тоже будет мусором. В этом случае под мусором понимается шум в наших данных.
Для обучения модели мы собираем огромное количество данных, чтобы помочь машине лучше учиться. Обычно большая часть собранных данных представляет собой шум, в то время как некоторые столбцы нашего набора данных могут не вносить существенного вклада в производительность нашей модели. Кроме того, наличие большого количества данных может замедлить процесс обучения и привести к замедлению работы модели. Модель также может учиться на этих нерелевантных данных и быть неточной.
Выбор функций — это то, что отличает хороших специалистов по данным от остальных. При той же модели и вычислительных мощностях, почему некоторые люди побеждают в соревнованиях с более быстрыми и точными моделями? Ответ — выбор функций. Помимо выбора правильной модели для наших данных, нам нужно выбрать правильные данные для включения в нашу модель.
Рассмотрим таблицу, содержащую информацию о старых автомобилях. Модель решает, какие автомобили нужно раздавить на запчасти.
Рисунок 1: Набор данных о старых автомобилях
В приведенной выше таблице мы видим, что модель автомобиля, год выпуска и пробег очень важны для определения того, достаточно ли старый автомобиль, чтобы его раздавить, или нет. Однако имя предыдущего владельца автомобиля не решает, следует ли раздавить машину или нет. Кроме того, это может запутать алгоритм в поиске закономерностей между именами и другими функциями. Следовательно, мы можем удалить столбец.
Рисунок 2. Удаление столбцов для выбора функций
Что такое выбор функции?
Выбор функции — это метод уменьшения входной переменной в вашу модель путем использования только релевантных данных и избавления от шума в данных.
Это процесс автоматического выбора соответствующих функций для вашей модели машинного обучения в зависимости от типа проблемы, которую вы пытаетесь решить. Мы делаем это, включая или исключая важные функции, не изменяя их. Это помогает сократить шум в наших данных и уменьшить размер наших входных данных.
Рисунок 3: Выбор функции
Модели с выбором функций
Модели выбора функций бывают двух типов:
- Контролируемые модели: контролируемый выбор функций относится к методу, который использует выходной класс меток для выбора функций. Они используют целевые переменные для определения переменных, которые могут повысить эффективность модели .
- Неконтролируемые модели: неконтролируемый выбор элементов относится к методу, которому не требуется выходной класс меток для выбора элементов. Мы используем их для неразмеченных данных.
Рисунок 4: Модели с выбором функций
Далее мы можем разделить контролируемые модели на три:
1. Метод фильтрации : В этом методе функции отбрасываются в зависимости от их отношения к выходным данным или того, как они соотносятся с выходными данными. Мы используем корреляцию, чтобы проверить, положительно или отрицательно коррелируют функции с выходными метками, и соответственно отбрасываем функции. Например: прирост информации, критерий хи-квадрат, показатель Фишера и т. д.
Рис. 5. Блок-схема метода фильтрации
2. Метод обертки : мы разделяем наши данные на подмножества и обучаем модель, используя это. На основе выходных данных модели мы добавляем и вычитаем функции и снова обучаем модель. Он формирует подмножества с использованием жадного подхода и оценивает точность всех возможных комбинаций признаков. Например: прямой выбор, обратное исключение и т. д.
Рис. 6. Блок-схема метода оболочки
3. Внутренний метод : Этот метод сочетает в себе качества методов фильтра и метода оболочки для создания наилучшего подмножества.
Рис. 7. Блок-схема внутренней модели
Этот метод заботится об итеративном процессе машинного обучения, сохраняя при этом минимальные затраты на вычисления. Например: регрессия лассо и хребта.
Как выбрать модель выбора функций?
Откуда мы знаем, какая модель выбора признаков подойдет для нашей модели? Процесс относительно прост, модель зависит от типов входных и выходных переменных.
Переменные бывают двух основных типов:
- Числовые переменные: включают целые числа, числа с плавающей запятой и числа.
- Категориальные переменные: в том числе метки, строки, логические переменные и т. д.
В зависимости от того, есть ли у нас числовые или категориальные переменные в качестве входных и выходных данных, мы можем выбрать нашу модель выбора признаков следующим образом:
Входная переменная | Выходная переменная | Модель выбора функций |
Цифровой | Цифровой |
|
Цифровой | Категориальный |
|
Категориальный | Цифровой |
|
Категориальный | Категориальный |
|
0003
Выбор функций с помощью Python
Давайте получим практический опыт в выборе характеристик, работая с набором данных Коби Брайанта, который анализирует удары, сделанные Коби из разных областей корта, чтобы определить, какие из них попадут в корзину.
Набор данных выглядит следующим образом:
Рис. 8. Набор данных Коби Брайанта
Как мы видим, в наборе данных 25 разных столбцов. Все они нам не понадобятся.
Сначала мы загружаем необходимые модули.
Рис. 9. Импорт модулей
Во-первых, давайте проверим столбцы loc_x и loc_y. Они, вероятно, представляют долготу и широту.
Рис. 10. График столбцов широты и долготы в нашем наборе данных
На рисунке показано:
Рисунок 11. Отображение широты и долготы
Из приведенных выше рисунков видно, что они напоминают две буквы «D» на баскетбольной площадке. Вместо двух отдельных столбцов мы можем преобразовать координаты в полярную форму и получить один столбец [‘угол’].
Рис. 12. Преобразование широты и долготы в полярную форму
Мы можем объединить столбцы минут и секунд в один столбец времени.
Рис. 13. Объединение двух столбцов
Давайте посмотрим на уникальные значения в столбцах team_id и team_name:
Рис. 14. Уникальные значения в параметрах team_id и team_name
Весь столбец содержит только одно значение и может быть удален. Давайте посмотрим на столбцы «match_up» и «opponent»:
.Рис. 15. Столбцы «match_up» и «opponent»
Опять же, они содержат одинаковую информацию. Давайте нанесем значения столбцов «dist» и «shot_distance» на один и тот же график, чтобы увидеть, чем они отличаются:
Рис. 16. График столбцов «dist» и «shot_distance»
Опять же, они содержат точно такую же информацию. Давайте посмотрим на столбцы shot_zone_area, shot_zone_basic и shot_zone_range.
Рисунок 17. График различных столбцов зон выстрела
На рисунке ниже показаны графики:
Рис. 18. Различные зоны выстрела
Мы видим, что они содержат разные части корта, откуда были сделаны выстрелы. Эта информация уже существует в столбцах угла и расстояния.
Теперь давайте отбросим все бесполезные столбцы.
Рис. 19. Удаление столбцов
После объединения столбцов и удаления ненужных столбцов мы получаем набор данных, содержащий только 11 важных столбцов.
Рис. 20. Окончательный набор данных
Изучите основы объектно-ориентированного программирования, веб-разработки с помощью Django и многого другого с помощью программы последипломного образования Калифорнийского технологического института по искусственному интеллекту и машинному обучению. Зарегистрируйтесь сейчас!
Заключение
В этой статье под названием «Все, что вам нужно знать о выборе функций» мы поняли, насколько важно выбрать лучшие функции для нашей модели машинного обучения. Затем мы рассмотрели, что такое выбор признаков, и некоторые модели выбора признаков. Затем мы перешли к простому способу выбора правильной модели выбора признаков на основе входных и выходных значений. Наконец, мы увидели, как реализовать выбор функций в Python с помощью демонстрации. Если вы хотите узнать больше о выборе функций и связанных с ними фундаментальных функциях Python, вам идеально подойдет сертификационный курс Simplielarn Python. Этот сертификационный курс Python охватывает основные принципы Python, включая операции с данными, условные операторы, сценарии оболочки, Django и многое другое, и готовит вас к успешной карьере профессионального программиста Python.
Была ли вам полезна эта статья о выборе функций? У вас есть сомнения или вопросы к нам? Упомяните их в разделе комментариев к этой статье, и наши эксперты ответят на них в ближайшее время!
Особенности метода| Скала 3 — Книга
Информация: JavaScript в настоящее время отключен, вкладки с кодом будут по-прежнему работать, но предпочтения не будут запоминаться.
В этом разделе представлены различные аспекты определения и вызова методов в Scala 3.
Определение методов
Методы Scala имеют множество функций, включая следующие:
- Общие (типовые) параметры
- Значения параметров по умолчанию
- Несколько групп параметров
- Контекстные параметры
- Параметры по имени
- и более…
Некоторые из этих функций демонстрируются в этом разделе, но когда вы определяете «простой» метод, который не использует эти функции, синтаксис выглядит следующим образом:
определение имя_метода (параметр1: тип1, параметр2: тип2): ReturnType = { // тело метода // идет сюда }
def имя_метода (параметр 1: тип 1, параметр 2: тип 2): тип возврата = // тело метода // идет сюда end имя_метода // это не обязательно
В этом синтаксисе:
- Ключевое слово
def
используется для определения метода - Стандарт Scala должен именовать методы, используя соглашение о регистре верблюда
- Параметры метода всегда определяются с типом
- Объявление возвращаемого типа метода необязательно
- Методы могут состоять из многих строк или только из одной строки
- Предоставление части
end methodName
после тела метода также необязательно и рекомендуется только для длинных методов
Вот два примера однострочного метода с именем add
, который принимает два входных параметра Int
. Первая версия явно показывает возвращаемый тип метода Int
, а вторая — нет:
по определению добавить (a: Int, b: Int): Int = a + b def add(a: Int, b: Int) = a + b
Рекомендуется аннотировать общедоступные методы с их типом возвращаемого значения. Объявление возвращаемого типа может упростить его понимание, когда вы будете смотреть на него месяцы или годы спустя или когда будете смотреть на код другого человека.
Вызов методов
Вызов метода прост:
val x = add(1, 2) // 3
Классы коллекций Scala имеют десятки встроенных методов. Эти примеры показывают, как их вызывать:
знач х = Список(1, 2, 3) х.размер // 3 x.contains(1) // правда x.map(_ * 10) // Список(10, 20, 30)
Примечание:
-
размер
не принимает аргументов и возвращает количество элементов в списке - Метод
содержит
, принимает один аргумент, значение для поиска -
карта
принимает один аргумент, функцию; в этом случае в него передается анонимная функция
Многострочные методы
Если метод длиннее одной строки, начинайте тело метода со второй строки с отступом вправо:
def addThenDouble(a: Int, b: Int): Int = { // представьте, что это тело требует нескольких строк сумма значений = a + b сумма * 2 }
def addThenDouble(a: Int, b: Int): Int = // представьте, что это тело требует нескольких строк сумма значений = a + b сумма * 2
В этом методе:
-
сумма
— неизменяемая локальная переменная; к нему нельзя получить доступ за пределами метода - Последняя строка удваивает значение
в сумме
; это значение возвращается из метода
Когда вы вставите этот код в REPL, вы увидите, что он работает как надо:
scala> addThenDouble(1, 1) res0: Целое = 4
Обратите внимание, что нет необходимости в операторе return
в конце метода. Потому что почти все в Scala представляет собой выражение — это означает, что каждая строка кода возвращает (или оценивается как ) значение — нет необходимости использовать return
.
Это станет более понятным, если вы сократите этот метод и напишите его в одной строке:
def addThenDouble(a: Int, b: Int): Int = (a + b) * 2
Тело метода может использовать все различные возможности языка:
-
if
/else
выражения -
совпадение
выражений -
пока
петель -
на
циклов идля
выражений - Присвоение переменной
- Вызовы других методов
- Определения других методов
В качестве примера реального многострочного метода этот метод getStackTraceAsString
преобразует входной параметр Throwable
в правильно отформатированный String
:
def getStackTraceAsString(t: Throwable): String = { val sw = новый StringWriter() t. printStackTrace (новый PrintWriter (sw)) sw.toString }
def getStackTraceAsString(t: Throwable): String = val sw = StringWriter() t.printStackTrace (PrintWriter (sw)) sw.toString
В этом методе:
- Первая строка назначает новый экземпляр
StringWriter
связывателю значенийsw
- Вторая строка сохраняет содержимое трассировки стека в
StringWriter
- Третья строка дает представление
String
трассировки стека
Значения параметров по умолчанию
Параметры метода могут иметь значения по умолчанию.
В этом примере для параметров timeout
и протокола
заданы значения по умолчанию:
def makeConnection(timeout: Int = 5_000, protocol: String = "http") = { println(f"время ожидания = ${время ожидания}%d, протокол = ${протокол}%s") // здесь больше кода... }
def makeConnection (время ожидания: Int = 5_000, протокол: String = "http") = println(f"время ожидания = ${время ожидания}%d, протокол = ${протокол}%s") // здесь больше кода. ..
Поскольку параметры имеют значения по умолчанию, метод можно вызвать следующими способами:
makeConnection() // тайм-аут = 5000, протокол = http makeConnection(2_000) // время ожидания = 2000, протокол = http makeConnection(3_000, "https") // время ожидания = 3000, протокол = https
Вот несколько ключевых моментов об этих примерах:
- В первом примере аргументы не предоставляются, поэтому метод использует значения параметров по умолчанию, равные 9.0326 5_000 и
http
- Во втором примере
2_000
предоставляется для значения тайм-аута - В третьем примере значения указаны для обоих параметров, поэтому они оба используются
Обратите внимание, что при использовании значений параметров по умолчанию потребителю кажется, что он может использовать три различных переопределенных метода.
Именованные параметры
При желании вы также можете использовать имена параметров метода при вызове метода.
Например, makeConnection
также можно вызвать следующими способами:
makeConnection(timeout=10_000) установить соединение (протокол = "https") makeConnection (время ожидания = 10_000, протокол = "https") makeConnection(протокол="https", время ожидания=10_000)
В некоторых платформах интенсивно используются именованные параметры. Они также очень полезны, когда несколько параметров метода имеют один и тот же тип:
заниматься (правда, правда, правда, ложь)
Без помощи IDE этот код может быть трудночитаемым, но этот код гораздо более четкий и очевидный:
включать( скоростьИссет = истина, направлениеИссет = истина, picardSaidMakeItSo = правда, TurnOffParkingBrake = ложь )
Предложение о методах, которые не принимают параметров
Когда метод не принимает параметров, говорят, что он имеет уровень arity arity-0 . Точно так же, когда метод принимает один параметр, это арность-1 метод. При создании методов arity-0:
- Если метод выполняет побочные эффекты, такие как вызов
println
, объявите метод с пустыми скобками - Если метод не выполняет побочных эффектов, таких как получение размера коллекции, что аналогично доступу к полю в коллекции, оставьте скобки выключенными
Например, у этого метода есть побочный эффект, поэтому он объявлен с пустыми скобками:
по умолчанию говорить() = println("привет")
Для этого требуется, чтобы вызывающие метод использовали открытые круглые скобки при вызове метода:
говорить // ошибка: "метод говорить должен быть вызван с аргументом ()" говорить() // печатает "привет"
Хотя это всего лишь соглашение, его соблюдение значительно улучшает читабельность кода: с первого взгляда становится легче понять, что метод с нулевой арностью имеет побочные эффекты.
Использование
if
в качестве тела метода Поскольку выражения if
/ else
возвращают значение, их можно использовать в качестве тела метода.
Вот метод с именем isTruthy
, который реализует определения Perl true
и false
:
def isTruthy(a: Any) = { если (а == 0 || а == "" || а == ложь) ЛОЖЬ еще истинный }
def isTruthy(a: Любой) = если а == 0 || а == "" || а == ложь тогда ЛОЖЬ еще истинный
Эти примеры показывают, как работает этот метод:
Истина (0) // ложь Истинно("") // Ложь isTruthy("привет") // правда Истина(1.0) // правда
Использование
match
в качестве тела метода Выражение match
также может использоваться в качестве всего тела метода, что часто и происходит.
Вот еще одна версия isTruthy
, написанная с выражением match
:
def isTruthy(a: Any) = a match { случай 0 | "" | ложь => ложь случай _ => правда }
def isTruthy(a: Matchable) = совпадение случай 0 | "" | ложь => ложь случай _ => правда
Этот метод работает так же, как и предыдущий метод, в котором использовалось выражение
if
/else
. Мы используемMatchable
вместоAny
в качестве типа параметра, чтобы принимать любое значение, поддерживающее сопоставление с образцом.
Дополнительные сведения о признаке
Matchable
см. в справочной документации.
Управление видимостью в классах
В классах, объектах, типажах и перечислениях методы Scala по умолчанию общедоступны, поэтому созданный здесь экземпляр Dog
может получить доступ к методу speak
:
class Dog { def speak() = println("Гав") } val d = новая собака d.speak() // печатает "Гав"
класс Собака: def speak() = println("Гав") val d = новая собака d.speak() // печатает "Гав"
Методы также могут быть помечены как private
.
Это делает их частными для текущего класса, поэтому их нельзя вызывать или переопределять в подклассах:
class Animal { private def breath() = println("Я дышу") } класс Cat расширяет Animal { // этот метод не будет компилироваться override def breath() = println("Эй, я полностью дышу") }
класс Животное: private def breath() = println("Я дышу") класс Cat расширяет Animal: // этот метод не будет компилироваться override def breath() = println("Эй, я полностью дышу")
Если вы хотите сделать метод закрытым для текущего класса, а также разрешить подклассам вызывать или переопределять его, пометьте метод как protected
, как показано с методом speak
в этом примере:
class Animal { private def breath() = println("Я дышу") деф ходить () = { дышать() println("Я иду") } защищенный def speak() = println("Привет?") } класс Cat расширяет Animal { переопределить def speak() = println("Мяу") } val cat = новый кот кошка. прогулка() кошка.говорить() cat.breathe() // не будет компилироваться, потому что он приватный
класс Животное: private def breath() = println("Я дышу") деф ходить() = дышать() println("Я иду") защищенный def speak() = println("Привет?") класс Cat расширяет Animal: переопределить def speak() = println("Мяу") val cat = новый кот кошка.прогулка() кошка.говорить() cat.breathe() // не будет компилироваться, потому что он приватный
Параметр protected
означает:
- К методу (или полю) могут обращаться другие экземпляры того же класса
- Не виден другим кодом в текущем пакете
- Доступен для подклассов
Объекты могут содержать методы
Ранее вы видели, что трейты и классы могут иметь методы.
Ключевое слово Scala object
используется для создания одноэлементного класса, и объект также может содержать методы.
Это хороший способ сгруппировать набор «служебных» методов. Например, этот объект содержит набор методов, работающих со строками:
object StringUtils { /** * Возвращает строку, аналогичную входной строке, но * усекается до указанной длины. */ def truncate(s: String, length: Int): String = s.take(length) /** * Возвращает true, если строка содержит только буквы и цифры. */ def letterAndNumbersOnly_?(s: String): Boolean = s.matches("[a-zA-Z0-9]+") /** * Возвращает true, если данная строка содержит пробелы * совсем. Предполагается, что `s` не равно нулю. */ def содержит пробелы (s: String): Boolean = s.matches(".*\\s.*") }
объект StringUtils: /** * Возвращает строку, аналогичную входной строке, но * усекается до указанной длины. */ def truncate(s: String, length: Int): String = s.take(length) /** * Возвращает true, если строка содержит только буквы и цифры. */ def letterAndNumbersOnly_?(s: String): Boolean = s.matches("[a-zA-Z0-9]+") /** * Возвращает true, если данная строка содержит пробелы * совсем. Предполагается, что `s` не равно нулю. */ def содержит пробелы (s: String): Boolean = s.matches(".*\\s.*") конец StringUtils
Методы расширения
Есть много ситуаций, когда вы хотели бы добавить функциональность к закрытым классам.
Например, представьте, что у вас есть класс Circle
, но вы не можете изменить его исходный код.
Это может быть определено в сторонней библиотеке следующим образом:
класс случая Circle(x: двойной, y: двойной, радиус: двойной)
Если вы хотите добавить методы в этот класс, вы можете определить их как методы расширения, например:
неявный класс CircleOps(c: Circle) { def окружность: Double = c.radius * math.Pi * 2 диаметр защиты: Double = c.radius * 2 площадь защиты: Double = math.Pi * c.radius * c.radius }
В Scala 2 используйте неявный класс
, подробнее читайте здесь.
(c: Круг) def окружность: Double = c.