Какие функции вызывает Swift? Часть 1: Возвращаемые значения

Это русский перевод статьи airspeedvelocity

“Which function does Swift call? Part 1: Return Values “.

Тип данных ClosedInterval “стесняется”. Вам придется уговаривать его выйти из-за спины своего друга, Range.

// r будет иметь тип Range<Int>
let r = 1...5

// если вам нужен тип ClosedInterval<Int>, вам
// следует определить его явно:
let integer_interval: ClosedInterval = 1...5

// Но если вам нужен doubles, то нет необходимости
// декларировать его явно.
// floatingpoint_interval is a ClosedInterval<Double>
let floatingpoint_interval = 1.0...5.0

Начиная с  Swift 1.0 beta 5, Range поддерживается исключительно для представления диапазонов индексов в коллекции.

Если вы не работаете с индексами, тип ClosedInterval, возможно, это то, что вам нужно. У него есть методы, подобные contains, которые за фиксированное ( независящее от длины интервала ) время определяют, содержит ли интервал некоторое значение:

let integer_interval: ClosedInterval = 1...5
integer_interval.contains(3)

Тип Range может использовать только обобщенный алгоритм contains

let r = 1..<6
contains (r,3)

, который сначала превратит r в последовательность, а потом выполнит итерацию по ней, и … мы получим цикл в цикле!

Но Range не уходит тихо. Если вы используете оператор ... с целыми числами, он расталкивает ClosedRange локтями и несется на сцену. Почему? Потому что целые числа являются “поисковыми индексами” (forward indexes) (именно так они используются в Array), и Swift оператор ... имеет 3 объявления:

func ...<T : Comparable>(start: T, end: T) -> ClosedInterval<T>

func ...<Pos : ForwardIndexType>(minimum: Pos, maximum: Pos) -> Range<Pos>

func ...<Pos : ForwardIndexType where Pos : Comparable>(start: Pos, end: Pos) -> Range<Pos>

Когда вы пишите let r = 1...5, Swift вызывает последнее объявление оператора ..., и вам возвращается Range. Для того, чтобы понять почему, нам необходимо пройти через различные способы, какими Swift решает, какие “перегруженные” (overloaded) функции вызывать. Этих способов очень много.

Давайте начнем с:

Различные возвращаемые значения

В Swift,  вы не можете объявить ту же самую функцию дважды:

func f() {  }

// error: Неверное переопределение f()
func f() {  }

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

struct A { }
struct B { }

func f() -> A { return A() }

// вторая  f работает, даже если они отличаются только
// тем, что они возвращают:
func f() -> B { return B() } 

// заметьте, что typealiases это только алиасы и не других типов,
// так что это не будет работать:
typealias L = A
// error: Invalid redeclaration of 'f()'
func f() -> L { return A() }

Если вызываются такие “отличающиеся только возвращаемым значением” функции, то нам нужно дать Swift достаточно информации при вызове, чтобы однозначно определить, какую функцию вызывать:

// error: Ambiguous use of 'f'(неодназначное использование f)
let x = f()

// вместо этого вы должны специфицировать, какое возвращаемое
// значение вам нужно:
let x: A = f()    // вызывается the A-returning версия
                  // x имеет тип A

// или если вы предпочитаете такой синтаксис:
let y = f() as B  // вызывается the B-returning версия
                  // y имеет тип B

// или, возможно, вы передаете его как аргумент в функции:
func takesA(a: A) { }

// the A-returning версия f будет вызвана
takesA(f())

Наконец, если вы хотите декларировать ссылку на функцию f, вам необходимо определить полный тип функции. Заметьте, что как только вы сделали это присвоение, вы получили ссылку на специфицированную функцию. В дальнейшем вам не надо заботиться об устранении “неоднозначности”:

// g будет ссылкой на the A-returning версию
let g: ()->A = f

// h будет ссылкой на the B-returning версию
let h = f as ()->B

// вызов g не требует больше информации, он ссылается
// на специфицированную функцию f, и z будет иметь тип A:
let z = g()

Итак,  ... это функция, которая отличается возвращаемым типом – она возвращает либо ClosedInterval, либо Range. Для явного получения результата типа ClosedInterval, мы можем заставить Swift вызвать функцию, которая будет возвращать требуемый тип.

Но если мы не будем явно определять для функции ... тип возвращаемого значeния, мы не получим ошибку “неоднозначности” (ambiguity), как видно из приведенного выше примера. Вместо этого, Swift по умолчанию выберит версию Range. Как так?

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

Leave a Reply

Your email address will not be published. Required fields are marked *