Scala. Профессиональное программирование 2022. Одерски Мартин, Спун Лекс, Веннерс Билл, Соммерс ФрэнкО41 Scala. Профессиональное программирование. 5е изд спб. Питер, 2022. 608 с. ил. Серия Библиотека программиста
Скачать 6.24 Mb.
|
Таблица 24.4. Операции в трейте Buffer Что Что делает Добавления buf += x (или buf.append(x) ) Добавляет элемент x в конец buf и возвращает в ка честве результата сам buf buf ++= xs (или buf.appendAll(xs) ) Добавляет в конец буфера все элементы xs x +=: buf (или buf.prepend(x) ) Добавляет элемент x в начало буфера xs ++=: buf (или buf.prependAll(xs) ) Добавляет в начало буфера все элементы xs buf.insert(i, x) Вставляет элемент x в то место в буфере, на которое указывает индекс i buf.insertAll(i, xs) Вставляет все элементы xs в то место в буфере, на которое указывает индекс i buf.padToInPlace(n, x) Добавляет в буфер элементы x , пока общее количе ство его элементов не достигнет n Удаления buf –= x (или buf.subtractOne(x) ) Удаляет из буфера элемент x buf ––= x (или buf.subtractAll(xs) ) Удаляет из буфера все элементы xs buf.remove(i) Удаляет из буфера элемент с индексом i buf.remove(i, n) Удаляет из буфера n элементов, начиная с элемента с индексом i buf.trimStart(n) Удаляет из буфера первые n элементов buf.trimEnd(n) Удаляет из буфера последние n элементов buf.clear() Удаляет из буфера все элементы Замена: buf.patchInPlace(i, xs, n) Заменяет (максимум) n элементов буфера элемен тами из xs , начиная с индекса i Копирование buf.clone() Новый буфер с теми же элементами, что и в buf 530 Глава 24 • Углубленное изучение коллекций 24 .5 . Множества Коллекции Set — это итерируемые Iterable коллекции, которые не содер жат повторяющихся элементов. Общие операции над множествами сведены в табл. 24.5, в табл. 24.6 показаны операции для неизменяемых множеств, а в табл. 24.7 — операции для изменяемых множеств. Операции разбиты на следующие категории. z z Проверки contains, apply и subsetOf. Метод contains показывает, со держит ли множество заданный элемент. Метод apply для множества является аналогом contains , поэтому set(elem) — то же самое, что и set contains elem . Следовательно, множества могут также использоваться в качестве тестовых функций, возвращающих true для содержащихся в них элементов, например: val fruit = Set("apple", "orange", "peach", "banana") fruit("peach") // true fruit("potato") // false z z Добавления + (псевдоним: incl) и ++ (псевдоним: concat) добавляют в множество один и более элементов, возвращая в качестве результата новое множество. z z Удаления - (псевдоним: excl) и -- (псевдоним: removedAll) удаляют из множества один и более элементов, возвращая новое множество. z z Операции над множествами для объединения, пересечения и разности множеств. Существуют в двух формах: текстовом и символьном. К тек стовым относятся версии intersect , union и diff , а к символьным — & , | и & . Оператор ++ , наследуемый Set из Iterable , может рассматриваться в качестве еще одного псевдонима union или | , за исключением того, что ++ получает IterableOnce аргумент, а union и | получают множества. Таблица 24.5. Операции в трейте Set Что Что делает Проверки xs.contains(x) Проверяет, является ли x элементом xs xs(x) Делает то же самое, что и xs contains x xs.subsetOf(ys) Проверяет, является ли xs подмножеством ys Удаления xs.empty Пустое множество того же класса, что и xs 24 .5 . Множества 531 Что Что делает Бинарные операции xs & ys (или xs.intersect(ys) ) Пересечение множеств xs и ys xs | ys (или xs.union(ys) ) Объединение множеств xs и ys xs & ys (или xs.diff(ys) ) Разность множеств xs и ys Неизменяемые множества предлагают методы добавления и удаления эле ментов путем возвращения новых множеств, которые сведены в табл. 24.6. Таблица 24.6. Операции в трейте immutable .Set Что Что делает Добавления xs + x (или xs.incl(x) ) Множество, содержащее все элементы xs и эле мент x xs ++ ys (или xs.concat(ys) ) Множество, содержащее все элементы xs и все элементы ys Удаления xs – x (или xs.excl(x) ) Множество, содержащее все элементы xs , кроме x xs –– ys (или xs.removedAll(ys) ) Множество, содержащее все элементы xs , кроме элементов множества ys У изменяемых множеств есть методы, которые добавляют, удаляют и обнов ляют элементы, которые сведены в табл. 24.7. Таблица 24.7. Операции в трейте mutable .Set Что Что делает Добавления xs += x (или xs.addOne(x) ) Добавляет элемент x в множество xs как побочный эффект и возвращает само множество xs xs ++= ys (или xs.addAll(ys) ) Добавляет все элементы ys в множество xs как по бочный эффект и возвращает само множество xs 532 Глава 24 • Углубленное изучение коллекций Что Что делает xs.add(x) Добавляет элемент x в xs и возвращает true , если x прежде не был в множестве, или false , если уже был Удаления xs –= x (или xs.subtractOne(x) ) Удаляет элемент x из множества xs как побочный эффект и возвращает само множество xs xs ––= ys (или xs.subtractAll(ys) ) Удаляет все элементы ys из множества xs как побоч ный эффект и возвращает само множество xs xs.remove(x) Удаляет элемент x из xs и возвращает true , если x прежде уже был в множестве, или false , если его прежде там не было xs.filterInPlace(p) Сохраняет только те элементы в xs , которые удов летворяют условию p xs.clear() Удаляет из xs все элементы Обновление xs(x) = b (или после раскрытия xs.update(x, b) ) Если аргумент b типа Boolean имеет значение true , то добавляет x в xs , в противном случае удаляет x из xs Клонирование xs.clone() Возвращает новое изменяемое множество с такими же элементами, как и в xs Операция s += elem в качестве побочного эффекта добавляет elem во множе ство s и в качестве результата возвращает измененное множество. По ана логии с этим s -= elem удаляет элемент elem из множества и возвращает в качестве результата измененное множество. Помимо += и -= , есть также операции над несколькими элементами ++= и --= , которые добавляют или удаляют все элементы Iterable или итератора. Выбор в качестве имен методов += и -= означает, что очень похожий код может работать как с изменяемыми, так и с неизменяемыми множествами. Рассмотрим сначала следующий интерпретатор, в котором используется неизменяемое множество s : var s = Set(1, 2, 3) s += 4 s = 2 s // Set(1, 3, 4) Таблица 24.7 (окончание) 24 .5 . Множества 533 В этом примере в отношении var переменной типа immutable.Set использу ются методы += и -= . Согласно объяснениям, которые были даны в шаге 10 главы 3, инструкции вида s += 4 — это сокращенная форма записи для s = s + 4 . Следовательно, в их выполнении участвует еще один метод + , применяемый в отношении множества s , а затем результат присваивается переменной s . А теперь рассмотрим аналогичную работу в интерпретаторе с изменяемым множеством: val s = collection.mutable.Set(1, 2, 3) s += 4 // Set(1, 2, 3, 4) s = 2 // Set(1, 3, 4) s // Set(1, 3, 4) Конечный эффект очень похож на предыдущий диалог с интерпретатором: начинаем мы с множеством Set(1, 2, 3) , а заканчиваем с множеством Set(1, 3, 4) . Но даже притом что инструкции выглядят такими же, как и раньше, они выполняют несколько иные действия. Теперь инструкция s += 4 вызыва ет метод += в отношении значения s , которое представляет собой изменяемое множество, выполняя изменения на месте. Аналогично этому инструкция s -= 2 теперь вызывает в отношении этого же множества метод -= Сравнение этих двух диалогов позволяет выявить весьма важный принцип. Зачастую можно заменить изменяемую коллекцию, хранящуюся в val переменной, неизменяемой коллекцией, хранящейся в var переменной, и наоборот. Это работает по крайней мере до тех пор, пока нет псевдонимов ссылок на коллекцию, позволяющих заметить, обновилась она на месте или была создана новая коллекция. Изменяемые множества также предоставляют в качестве вариантов += и -= методы add и remove . Разница в том, что методы add и remove возвращают булев результат, показывающий, возымела ли операция эффект над множе ством. В текущей реализации по умолчанию изменяемого множества его элементы хранятся с помощью хештаблицы. В реализации по умолчанию неизменя емых множеств используется представление, которое адаптируется к ко личеству элементов множества. Пустое множество представляется в виде простого объектаодиночки. Множества размером до четырех элементов представляются в виде одиночного объекта, сохраняющего все элементы как поля. Все неизменяемые множества, имеющие большие размеры, реализуют ся в виде сжатых хешмассивов из сопоставленных префиксных деревьев 1 1 Префиксные деревья на основе сжатых хешмассивов описываются в разделе 24.7. 534 Глава 24 • Углубленное изучение коллекций Последствия применения таких вариантов представления заключаются в том, что для множеств небольших размеров с количеством элементов, не превышающим четырех, неизменяемые множества получаются более ком пактными и более эффективными в работе, чем изменяемые. Поэтому, если предполагается, что множество будет небольшим, попробуйте сделать его неизменяемым. 24 .6 . Отображения Коллекции типа Map представляют собой Iterable коллекции, состоящие из пар «ключ — значение», которые также называются отображениями или ассоциациями. Объект Predef в Scala предлагает неявное преобразование, позволяющее использовать запись вида ключ –> значение в качестве альтерна тивы синтаксиса для пары вида (ключ, значение) . Таким образом, выражение для инициализации Map("x" –> 24, "y" –> 25, "z" –> 26) означает абсолютно то же самое, что и выражение Map(("x", 24), ("y", 25), ("z", 26)) , но чи тается легче. Основные операции над отображениями, сведенные в табл. 24.8, похожи на аналогичные операции над множествами. Неизменяемые отображения под держивают дополнительные операции добавления и удаления, которые воз вращают новые отображения, как показано в табл. 24.9. Изменяемые отобра жения дополнительно поддерживают операции, перечисленные в табл. 24.10. Операции над отображениями разбиваются на следующие категории. z z Операции поиска apply, get, getOrElse, contains и isDefinedAt превра щают отображения в частично примененные функции от ключей к значе ниям. Основной метод поиска для отображений выглядит так: def get(key): Option[Value] Операция m.get(key) проверяет, содержит ли отображение ассоциацию для заданного ключа. Будучи в наличии, такая ассоциация возвращает значение ассоциации в виде объекта типа Some . Если такой ключ в ото бражении не определен, то get возвращает None . В отображениях также определяется метод apply , возвращающий значение, непосредственно ассоциированное с заданным ключом, без его инкапсуляции в Option Если ключ в отображении не определен, то выдается исключение. z z Добавления и обновления + (псевдоним: updated), ++ (псевдоним: concat) updateWith и updatedWith позволяют добавлять к отображению новые привязки или изменять уже существующие. 24 .6 . Отображения 535 z z Удаления - (псевдоним: removed) и -- (псевдоним: removedAll) позво ляют удалять привязки из отображения. z z Операции создания подколлекций keys, keySet, keysIterator, valu- esIterator и values возвращают по отдельности ключи и значения ото бражений в различных формах. z z Преобразования filterKeys и mapValues создают новое отображение путем фильтрации и преобразования привязок существующего отображения. Таблица 24.8. Операции в трейте Map Что Что делает Поиск ms.get(k) Значение Option , связанное с ключом k в отображе нии ms , или None , если ключ не найден ms(k) (или после рас крытия ms apply k ) Значение, связанное с ключом k в отображении ms , или выдает исключение, если ключ не найден ms.getOrElse(k, d) Значение, связанное с ключом k в отображении ms , или значение по умолчанию d , если ключ не найден ms.contains(k) Проверяет, содержится ли в ms отображение для ключа k ms.isDefinedAt(k) То же, что и contains Создание подколлекций ms.keys Iterableколлекция, содержащая каждый ключ, име ющийся в ms ms.keySet Множество, содержащее каждый ключ, имеющийся в ms ms.keysIterator Итератор, выдающий каждый ключ, имеющийся в ms ms.values Iterableколлекция, содержащая каждое значение, связанное с ключом в ms ms.valuesIterator Итератор, выдающий каждое значение, связанное с ключом в ms Преобразования ms.view.filterKeys(p) Представление отображения, содержащее только те отображения в ms , в которых ключ удовлетворяет условию p ms.view.mapValues(f) Представление отображения, получающееся в резуль тате применения функции f к каждому значению, связанному с ключом в ms 536 Глава 24 • Углубленное изучение коллекций Таблица 24.9. Операции в трейте immutable .Map Что Что делает Добавления и обновления ms + (k –> v) (или ms.updated(k, v) ) Отображение, содержащее все ассоциации ms , а также ассоциацию k –> v ключа k со значением v ms ++= kvs (или ms.concat(kvs) ) Отображение, содержащее все ассоциации ms , а также все пары «ключ — значение» из kvs ms.updatedWith(k)(f) Отображение с добавлением, обновлением или уда лением привязки для ключа k . Функция f принимает в качестве параметра значение, связанное в настоя щий момент с ключом k (или None , если такой привяз ки нет), и возвращает новое значение (или None для удаления привязки) Удаления ms – k (или ms.removed(k) ) Отображение, содержащее все ассоциации ms , за ис ключением тех, которые относятся к ключу k ms –– ks (или ms.removedAll(ks) ) Отображение, содержащее все ассоциации ms , за ис ключением тех, ключи которых входят в ks Таблица 24.10. Операции в трейте mutable .Map Что Что делает Добавления и обновления ms(k) = v (или после рас крытия ms.update(k, v) ) Добавляет в качестве побочного эффекта ассо циацию ключа k со значением v к отображе нию ms , перезаписывая все ранее имевшиеся ассоциации k ms += (k –> v) Добавляет в качестве побочного эффекта ассоциа цию ключа k со значением v к отображению ms и возвращает само отображение ms ms ++= kvs Добавляет в качестве побочного эффекта все ассо циации, имеющиеся в kvs , к ms и возвращает само отображение ms ms.put(k, v) Добавляет к ms ассоциацию ключа k со значением v и возвращает как Option любое значение, ранее связанное с k ms.getOrElseUpdate(k, d) Если ключ k определен в отображении ms , то воз вращает связанное с ним значение. В противном случае обновляет ms ассоциацией k –> d и возвра щает d 24 .6 . Отображения 537 Что Что делает ms.updateWith(k)(f) Добавляет, обновляет или удаляет ассоциацию с ключом k . Функция f принимает в качестве параметра значение, которое в настоящий момент связано с k (или None , если такой ассоциации нет), и возвращает новое значение (или None t при удале нии ассоциации) Удаления ms –= k Удаляет в качестве побочного эффекта ассоциацию с ключом k из ms и возвращает само отображение ms ms ––= ks Удаляет в качестве побочного эффекта все ассоциа ции из ms с ключами, имеющимися в ks , и возвраща ет само отображение ms ms.remove(k) Удаляет все ассоциации с ключом k из ms и воз вращает как Option любое значение, ранее связан ное с k ms.filterInPlace(p) Сохраняет в ms только те ассоциации, у которых ключ удовлетворяет условию p ms.clear() Удаляет из ms все ассоциации Преобразование и клонирование ms.mapValuesInPlace(f) Выполняет преобразование всех связанных значе ний в отображении ms с помощью функции f ms.clone() Возвращает новое изменяемое отображение с таки ми же ассоциациями, как и в ms Операции добавления и удаления для отображений — зеркальные отражения таких же операций для множеств. Неизменяемое отображение может быть преобразовано с помощью операций + , - и updated . Для сравнения: изменя емое отображение m можно обновить «на месте» двумя способами: m(key) = value и m += (key –> value) . Изменяемые отображения также поддерживают вариант m.put(key, value) , который возвращает значение Option , содержа щее то, что прежде ассоциировалось с ключом, или None , если ранее такой ключ в отображении отсутствовал. Метод getOrElseUpdate пригодится для обращения к отображениям там, где они действуют в качестве кэша. Скажем, у вас есть весьма затратное вычис ление, запускаемое путем вызова функции f : def f(x: String) = println("taking my time.") Thread.sleep(100) x.reverse 538 Глава 24 • Углубленное изучение коллекций Далее предположим, что у f нет побочных эффектов, поэтому ее повторный вы зов с тем же самым аргументом всегда будет выдавать тот же самый результат. В таком случае можно сберечь время, сохранив ранее вычисленные привязки аргумента и результата выполнения f в отображении, и вычислять результат выполнения f , только если результат для аргумента не был найден в отображе нии. Можно сказать, что отображение — это кэш для вычислений функции f : val cache = collection.mutable.Map[String, String]() Теперь можно создать более эффективную кэшированную версию функ ции f : scala> def cachedF(s: String) = cache.getOrElseUpdate(s, f(s)) def cachedF(s: String): String scala> cachedF("abc") taking my time. val res16: String = cba scala> cachedF("abc") val res17: String = cba Обратите внимание: второй аргумент getOrElseUpdate — это аргумент, пере даваемый по имени. Следовательно, показанное ранее вычисление f("abc") выполняется лишь в том случае, если методу getOrElseUpdate потребуется значение его второго аргумента, что происходит именно тогда, когда его первый аргумент не найден в кэширующем отображении. Вы могли бы также непосредственно реализовать cachedF , используя только основные операции с отображениями, но для этого понадобится дополнительный код: def cachedF(arg: String) = cache.get(arg) match case Some(result) => result case None => val result = f(arg) cache(arg) = result result 24 .7 . Конкретные классы неизменяемых коллекций В Scala на выбор предлагается множество конкретных классов неизменя емых коллекций. Друг от друга они отличаются реализуемыми трейтами (отображения, множества, последовательности) тем, могут ли они быть бес |