Переменные Swift: лучший способ обработать это строковое объявление?

#swift #xcode

#swift #xcode

Вопрос:

Я работаю с игровой площадкой, управляемой Swift, и наткнулся на этот пример.

Пример кода использует массив словарей (видов) и выполняет поиск по наибольшему числу. Упражнение состоит в том, чтобы найти как наибольшее число, так и вернуть вид числа.

 let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0

for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)
  

В этом примере 25 является наибольшим числом и имеет «квадратный» вид.

Моим непосредственным побуждением было добавить новую строковую переменную с именем «largestKind» и объявить ее после largest(Int).

 let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
var largestKind: String

for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
            largestKind = kind
        }
    }
}
print(largest)
print(largestKind) // error: Variable 'largestKind' used before being initialized
  

Однако Swift выдает эту ошибку, указывающую, что я не инициализировал largestKind перед его использованием. Я полагаю, что установка largestKind равной kind не считается инициализацией?

Я нашел два способа обойти эту ошибку:

  1. Инициализация largestKind строкой-заполнителем (т.Е. var largestKind = «»)
  2. Обработайте largestKind как необязательный, а затем разверните с помощью print (largestKind!)

Пример 1:

 let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
var largestKind = ""

for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
            largestKind = kind
        }
    }
}
print(largest)
print(largestKind)
  

Пример 2:

 let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
var largestKind: String?

for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
            largestKind = kind
        }
    }
}
print(largest)
print(largestKind!)
  

Что было бы наилучшей практикой для исправления этой ошибки? Кроме того, почему я получал ошибку инициализации, когда я устанавливал значение, равное другой переменной?

Комментарии:

1. Вы получили ошибку, потому что не было гарантии, что largestKind будет инициализирован. Кстати, в вашем примере 2 не разворачивайте ваш необязательный параметр принудительно. Если оно не инициализировано, ваше приложение завершит работу. Используйте if let largestKind = largestKind { или укажите значение по умолчанию с помощью оператора объединения nil largestKind ?? "none"

2.Если largestKind это необязательно, то largest также должно быть. Поскольку автор исходного кода решил просто начать largest с 0 , логическим эквивалентом для largestKind было бы пустое String "" значение. Для такого простого примера, когда вы знаете, что будет получено некоторое значение, я бы не стал заморачиваться с необязательным значением.

Ответ №1:

Я могу посмотреть на ваши данные и ваш код, и я вижу, что largestKind всегда будет инициализирован, потому что есть числа > 0.

Однако, согласно правилам, которые использует компилятор, нет гарантии, что largestKind когда-либо будет установлен. Вот почему вы получаете сообщение об ошибке во время компиляции: потому что, согласно компилятору, largestKind может не инициализироваться при его использовании.

Вы можете исправить это двумя способами: первый, вы инициализируете var largestKind = «» . Теперь largestKind всегда инициализируется. Во-вторых, вы объявляете переменную largestKind: String? Теперь это необязательная строка, и параметры всегда автоматически инициализируются значением nil.

Во втором случае, если вы вызываете print(largestKind!) вы говорите компилятору «пожалуйста, завершите работу моей программы во время выполнения, если largestKind равен нулю». Это несчастный случай, ожидающий своего часа. Принудительное разворачивание с использованием! следует использовать, только если вы абсолютно уверены, что оно не может быть равно нулю. Вы могли бы поставить галочку вокруг типа печати if let largestKind = largestKind { } или вы могли бы написать print (largestKind ?? "") , который будет печатать largestKind, если не nil, и пустую строку в противном случае.

Ответ №2:

Основываясь на вашем примере, я бы сказал, что var largestKind = «», потому что у вас всегда будет наибольшее значение вида. Однако в другой ситуации, когда это не гарантировано, вам следует использовать var largestKind: String?

Не уверен, что вы имеете в виду под своим 2-м вопросом. Ошибка возникает из-за var largestKind: String

Комментарии:

1. Вам не следует использовать "" . String? или даже String! было бы лучшим выбором, как в том виде, в котором написано в данный момент, так и для будущего случая, когда входные данные могут быть пустыми. Написание его как "" today заставило бы его работать сегодня, но странным образом беззвучно отказывать в будущем.

Ответ №3:

Вы получили ошибку, потому что не было гарантии, что largestKind будет инициализирован. Кстати, в вашем примере 2 не разворачивайте ваш необязательный параметр принудительно. Если оно не инициализировано, ваше приложение завершит работу.

Наилучшей практикой является развертывание вашего необязательного с помощью if let или guard let .

 if let largestKind = largestKind {
    // your code
}
  

или укажите значение по умолчанию, используя оператор объединения nil

 largestKind ?? "none"
  

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

 if let (kind, largest) = interestingNumbers.compactMapValues({$0.max()}).max(by: {$0.value < $1.value}) {
    print("kind:", kind, "largest:", largest)  // kind: Square largest: 25
}