Как проверить необязательность строки?

У меня есть два разных сценария, в которых мне нужно проверить «необязательность» необязательного типа. Мне не удалось понять, как явно проверить, является ли переменная .None или .Some, кроме как с громоздким оператором switch. Как я могу проверить Someness с помощью оператора if?

Сценарий 1

Я пишу средство форматирования адресов, и мои входные данные представляют собой ряд строк? типы. В этом примере будет работать простой тест на (str != nil). Однако, поскольку моя другая потребность связана с «двойным необязательным», а нулевой тест не может различить .Some(.None) и .None, решение этой проблемы решит и эту проблему.

Вот версия, которая работает с switch

let address1:String? = "123 Main St"
let address2:String? = nil
let apt:String? = "101"

let components = [address1, address2, apt].filter( { (c) -> Bool in
    switch c {
    case .Some: return true
    case .None: return false
    }
}).map { return $0! } //Had to map because casting directly to [String] crashes
print(", ".join(components)) //"123 Main St, 101"

Я бы хотел увидеть что-то вроде if:

let nice = ["123 Main St", nil, "303"].filter { (c) -> Bool in
    return (c == .Some)
}
print(", ".join(nice))

Сценарий 2

Вот где нулевой тест не сработает. Если что-то является строкой ?? это может быть любое из .None, .Some(.None) или .Some(.Some(String)). В моем случае переменная несет идентификатор записи из вызова API, который может отсутствовать полностью (.None), значение (.Some(.Some("ABDEFG")) или явно NULL (.Some(.None)).

let teamNoneNone: String?? = .None
let teamSomeNone: String?? = .Some(.None)
let teamSomeSome: String?? = "My favorite local sportsball team"

if teamNoneNone == nil {
    print("teamNoneNone is nil but is it .None? We don't know!") //prints
} else {
    print("teamNoneNone is not nil")    
}

if teamSomeNone == nil {
    print("teamSomeNone is nil")
} else {
    print("teamSomeNone is not nil but is it .Some(.None)? We don't know!") //prints
}

if teamSomeSome == nil {
    print("teamSomeSome is nil but is it .None? We don't know!")
} else {
    print("teamSomeSome is not nil but is it .Some(.None) or .Some(.Some())? We don't know!") //prints
}

В другом сообщении SO я нашел подобное обходное решение, но не очень понятно, что происходит со случайным читателем:

if let team: String? = teamSomeNone {
    print("teamSomeNone is Some(.None)") //prints
} else {
    print("teamSomeNone is .Some(.Some())")
}

person Chris Morse    schedule 16.04.2015    source источник
comment
Типы опций часто лучше обрабатывать, используя конструкцию switch / case imo. У каждого класса опций, который я когда-либо видел, есть метод _1 _ / _ 2_. В противном случае вы всегда можете определить такую ​​функцию с помощью переключателя.   -  person Carcigenicate    schedule 17.04.2015
comment
@Carcigenicate, у вас есть ссылка на этот метод isNone / isSome? Я искал документы Apple, SwiftDoc.org, пробовал во время выполнения и ничего не нашел. Если он существует, это именно то, что я ищу, но, похоже, это не так.   -  person Chris Morse    schedule 17.04.2015
comment
Извините, я сказал, что все языки, которые я знаю, которые имеют типы Option (Haskell, Scala, Java), имеют метод тестера; было бы странно, если бы не Свифт (я не знаю Свифт). Вы пытались оценить этот параметр как логическое? (if (aMaybeObj))   -  person Carcigenicate    schedule 17.04.2015
comment
Я только что наткнулся на это сообщение в блоге Дэвида Оуэнса II, посвященное этой проблеме, которая справляется с ней довольно хорошо, ИМХО. owensd.io/2015/05/12/optionals-if-let. html   -  person Chris Morse    schedule 15.05.2015


Ответы (1)


if let проверяет, равно ли значение .None, а если нет, разворачивает его и привязывает к локальной переменной в операторе if.

Использование switch с .Some и .None на самом деле является второстепенным способом обработки опциональных значений, если if let его не сокращает. Но это почти всегда так, особенно теперь, когда вы можете выполнять несколько if let в одном операторе, после последнего выпуска Swift 1.2 в производство.

Желание отфильтровать пустые значения в коллекции - достаточно распространенная задача, и в Haskell есть для нее стандартная функция, называемая catMaybe. Вот версия, которую я назову catSome, которая поможет в Swift:

func catSome<T>(source: [T?]) -> [T] {
    var result: [T] = []
    // iterate over the values
    for maybe in source {
        // if this value isn’t nil, unwrap it
        if let value = maybe {
            // and append it to the array
            result.append(value)
        }
    }
    return result
}

let someStrings: [String?] = ["123 Main St", nil, "101"]

catSome(someStrings)  // returns ["123 Main St", "101"]

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

Но если вы все же обнаружите, что у вас есть какие-то, и все, что вас волнует, это внутреннее значение, вы можете развернуть их, используя двойной if let:

// later parts of the let can rely on the earlier
if let outer = teamSomeSome, teamName = outer {
    println("Fully unwrapped team is \(teamName)")
}

Если вы хотите явно знать, имеет ли double-optional внутреннее nil внутри внешнего значения, но не nil само по себе, вы можете использовать if let с предложением where:

if let teamSomeMaybe = teamSomeNone where teamSomeMaybe == nil {
    // this will be executed only if it was .Some(.None)
    println("SomeNone")
}

Предложение where - это дополнительное условие, которое может применяться к развернутому значению.

person Airspeed Velocity    schedule 17.04.2015
comment
Спасибо за очень полный ответ! Я только вчера обновил свой проект до Swift 1.2, поэтому раньше не использовал предложение where. Это не так ясно, как мне бы хотелось, но определенно большое улучшение по сравнению с тем, что было у меня. Если никто не придумает более ясного ответа, я приму этот ответ. - person Chris Morse; 17.04.2015