Видеокурсы по изучению языка программирования Swift. Подробнее

Оговорка where

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

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

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

В примере ниже определяем универсальную функцию allItemMatch, которая проверяет, чтобы увидеть содержат ли два экземпляра Container одни и те же элементы в одной и той же последовательности. Функция возвращает значение типа Bool, то есть, если у нас все элементы и их последовательность совпадает, то функция возвращает true, если нет - false.

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

func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
        
        // Проверяем одинаковое ли количество элементов находится в контейнерах.
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // Проверяем все ли значения попарно равны.
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        
        // Все элементы совпадают, так что возвращаем true.
        return true
}

Эта функция принимает два аргумента someContainer и anotherContainer. Аргумент someContainer имеет тип C1, аргумент anotherContainer имеет тип C2. И C1 и C2 являются заполнителями имен типов для двух контейнеров, которые будут определены, когда будет вызвана функция.

Список типов параметров функции размещает следующие требования на два параметров типа:

  • C1 должен соответствовать протоколу Container (C1: Container)
  • C2 должен соответствовать протоколу Container (C2: Container)
  • ItemType для C1 должен быть тем же, что и ItemType для C2 (C1.ItemType == C2.ItemType)
  • ItemType для C1 должен соответствовать протоколу Equatable (C1.ItemType: Equatable)

Третье и четвертое требование определены как часть оговорки where, и записаны после ключевого слова where, в качестве части списка типов параметров функции.

Эти требования означают:

  • someCharacter является контейнером типа C1.
  • anotherCharacter является контейнером типа C2.
  • someCharacter и anotherCharacter содержат значения одного типа
  • Элементы в someContainer могут быть проверены при помощи оператора неравенства (!=), чтобы увидеть, что они отличаются друг от друга.

Третье и четвертое требование комбинируются так, чтобы элементы в anotherContainer так же могли бы быть проверены оператором !=, потому что они в точности одного и того же типа, что и в someContainer.

Эти требования позволяют функции allItemsMatch(_:_:) сравнивать два контейнера, даже если они являются контейнерами разного типа.

Функция allItemsMatch(_:_:) начинается с проверки количества элементов в этих контейнерах. Если они содержат разное количество элементов, то эти контейнеры уже не могут быть одинаковыми, функция возвращает false.

После проведения этой проверки, функция перебирает все элементы в someContainer при помощи for-in цикла и полуоткрытого оператора диапазона (..<). Для каждого элемента someContainer функция проверяет равенство элемента соответствующему элементу в контейнере anotherContainer. Если два элемента не равны друг другу, то эти два контейнера не считаются одинаковыми, функция возвращает false.

Если цикл закончился без каких-каких либо несоответствий элементов, то два контейнера считаются одинаковыми, и функция возвращает true.

Вот как выглядит функция allItemsMatch(_:_:) в действии:

var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
 
var arrayOfStrings = ["uno", "dos", "tres"]
 
if allItemsMatch(stackOfStrings, arrayOfStrings) {
    print("All items match.")
} else {
    print("Not all items match.")
}
// Выведет "All items match."

Пример выше создает экземпляр Stack для хранения значений типа String и добавляет три значения на стек. Так пример создает экземпляр Array, который инициализируется литералом массива, содержащего три одинаковые строки в стеке. Даже тогда стек и массив имеют разные типы, но оба они соответствую протоколу Container, и оба они содержат одинаковый тип значений. Тем не менее вы можете вызвать функцию allItemsMatch(_:_:) с этими двумя контейнерами в качестве своих аргументов. В примере выше функция allItemsMatch(_:_:) корректно извещает нас, что все элементы этих двух контейнеров одинаковые.

Swift: 
3.0