Синтаксис дополнительного типа Swift

Согласно руководству Apple Swift, когда у вас есть следующий массив и следующая функция для поиска в массиве:

let namesArray = ["John", "Lisa", "Bill", "Jennifer"]

// Note this function is set-up to return an OPTIONAL Int:
func findName (personName:String, arrayToSearchIn:String[]) -> Int? {
    for (indexOfName, nameValue) in enumerate(arrayToSearchIn) {
       if nameValue == personName {
          return indexOfName
       }
    }
    return nil
}

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

let nameIndex: Int? = findName("Lisa", arrayToSearchIn: namesArray)
if nameIndex {
    println("found \(namesArray[nameIndex!])")
}
else {
    println("NOT found")
}

Это все хорошо, но их следующий пример, в котором они оптимизируют код, объединяя 2 оператора в один, сбивает меня с толку, потому что они внезапно пропускают необязательный "?" и "!" из их синтаксиса:

if let nameIndex = findName("Lisa", arrayToSearchIn: namesArray) {
   println("found \(namesArray[nameIndex])")
}

И это отлично работает.

Так почему же в первом примере nameIndex было объявлено с "?" - вот так:

nameIndex:Int?

и принудительная распаковка происходит с использованием "!"

[nameIndex!]

но во втором примере ни "?" или "!" один используется в синтаксисе?


person sirab333    schedule 11.06.2014    source источник


Ответы (2)


Обработка объявления в if let особенная; следует обращаться с ним как с собственной языковой конструкцией (а не как с простой комбинацией if и let). В

if let nameIndex = findName("Lisa", arrayToSearchIn: namesArray) {
   println("found \(namesArray[nameIndex]")
}

тип nameIndex не нужно указывать, потому что он может быть выведен как возвращаемый тип findName(), который равен Int?. В теле if let ... значение nameIndex не связано с необязательным, поэтому разворачивание не требуется — значение будет Int.

Вот пример того, как обрабатывается привязка if let. Обратите внимание, что явное объявление типа по существу игнорируется:

> func test () -> Int? { return 111 }
> if let x : Int? = test () { return x }
$R6: Int? = 111
> if let x : Int = test () { return x }
$R7: Int = 111
> if let x = test () { return x }
$R8: Int = 111

но если вы попытаетесь быть таким «гибким» за пределами if let, вы получите ошибку от компилятора:

> let y : Int = test ()
<REPL>:33:15: error: value of optional type 'Int?' not unwrapped; 
   did you mean to use '!' or '?'?
person GoZoner    schedule 11.06.2014
comment
(Извините за редактирование) Итак, я понимаю, что вы говорите о том, что вам не нужно явно объявлять тип для nameIndex, потому что Type Inference позаботится об этом автоматически, но тогда это не должно применяться в оба случаи? Почему в первом случае мы делаем должны указать, что нам нужен необязательный элемент и что мы хотим принудительно развернуть его, но во втором, усеченном операторе if let, нам не нужно делать это? - person sirab333; 12.06.2014
comment
Это потому, что if let выполняет развертывание для вас, связывая nameIndex с его фактическим значением. - person GoZoner; 12.06.2014
comment
@sirab333: В вашем первом коде nameIndex имеет тип Int?. Вы можете явно указать тип или позволить ему вывести его; оба будут работать. Во втором коде nameIndex имеет тип Int. Опять же, вы можете указать это явно или позволить сделать вывод. Поскольку в первом случае это Int?, его нужно развернуть. Во втором случае это уже Int. - person newacct; 13.06.2014
comment
@newacct: это не сходится: в обоих случаях nameIndex в конечном итоге присваивается значение, возвращаемое функцией findName, которая явно объявлена ​​как возвращающая необязательный Int?, а не обычный Int. Таким образом, в обоих случаях не будет nameIndex также иметь тип Int? - либо потому, что он тоже был явно объявлен как таковой, либо потому, что он будет выведен как таковой с учетом что он фиксирует Int?, возвращаемый функцией? ИЛИ, вы говорите, что любая переменная, объявленная как обычная переменная, а не как необязательная, автоматически принудительно разворачивает все, что ей присваивается? - person sirab333; 13.06.2014
comment
@ sirab333: Во втором случае nameIndex не присваивается значение, возвращаемое функцией findName. Это специальный синтаксис if let. Это не то же самое, что выполнить let, а затем проверить переменную. В if let foo = bar, если bar имеет тип T?, то foo имеет тип T. foo назначается только в том случае, если bar не является nil, и только потом разворачивается в foo. - person newacct; 13.06.2014

? — это оператор для явного объявления необязательного типа. ! — это оператор для принудительного развертывания необязательных элементов. Синтаксис в примере — это специальное сокращение Swift для проверки и развертывания в одну краткую строку. В нем говорится: «присвойте nameIndex результату findName(...), и если это не nil, выполните следующий код: ...»

person Zev Eisenberg    schedule 11.06.2014
comment
Я ценю, что вы нашли время, чтобы ответить на этот вопрос, но я боюсь, что вы неправильно поняли мой вопрос. Я не спрашивал, чем занимаются ? и !. Я знаю, что они делают, и я полностью понимаю, что здесь используется стенография. Я спрашивал, почему ? и ! нужно было использовать в первом примере, а не во втором, сокращенном примере. Кажется, что вывод типов должен работать для обоих, а не только для одного, но не для другого. есть идеи? - person sirab333; 12.06.2014
comment
Я не знаю, как работает вывод типов в первом примере. Что произойдет, если вы удалите тип полностью? Я считаю, что если вы объявите его как Int вместо Int?, вы получите ошибку, но что, если вы полностью опустите тип: let nameIndex = findName("Lisa", arrayToSearchIn: namesArray)? - person Zev Eisenberg; 12.06.2014