У меня есть базовая пользовательская структура в Swift, где использование Any
для типов ввода далеко не идеально из-за статической типизации языка, которая может обойти проверку типов компилятором и привести к ошибкам и потере информации о типах.
Однако я столкнулся с необходимостью вернуть Any
из таких функций, как vectorAssoc
, особенно в случаях, когда тип возвращаемого значения может быть кортежем или логическим значением. Вот пример функций Vector
и vectorAssoc
:
struct Vector<Element>: ExpressibleByArrayLiteral {
private var storage: [Element] = []
public init(arrayLiteral elements: Element...) {
storage = elements
}
func count() -> Int {
return storage.count
}
subscript(index: Int) -> Element {
return storage[index]
}
}
func vectorAssoc(_ v: Int, _ vec: Vector<(Int, Int)>) -> Any {
func helper(_ i: Int) -> Any {
if i >= vec.count() {
return false
} else {
let elem = vec[i]
if elem.0 == v {
return elem
} else {
return helper(i + 1)
}
}
}
return helper(0)
}
let vec: Vector = [(2, 1), (3, 1), (4, 1), (5, 1)]
// Example usage
let a = vectorAssoc(4, vec) // prints (4, 1)
let b = vectorAssoc(12, vec) // prints false
Поскольку выходные данные vectorAssoc
могут использоваться в качестве входных данных для другой функции, я ищу предложения о том, как более эффективно обрабатывать возвращаемый тип Any
. В идеале я ищу способ указать, что тип возвращаемого значения может быть только одним из двух конкретных типов (кортеж или логическое значение), не прибегая к пользовательским типам или дополнительным параметрам.
Если существует способ преобразования такого Any
в примитивный тип, использующий преимущества функций безопасности типов Swift, я был бы очень признателен за любые идеи или улучшения этой настройки.
Итак, очень простое объяснение нового вопроса:
Any в данном случае — это ТОЛЬКО 2 типа (кортеж и логический), его не может быть больше двух. Если бы был способ вернуть Any (только два конкретных типа) и каким-то образом Any стал бы примитивным типом (одним из этих двух), который мог бы воспользоваться преимуществами безопасности типов. Есть ли способ заставить это работать?
Редактировать:
Но было бы неплохо иметь новый тип, что-то вроде:
func foo(_ param: in Type) -> out Type { ... }
Этот тип похож на Any в том, что он может представлять несколько типов, но в этом случае он будет ограничен только двумя типами: кортежем и логическим значением. Вам необходимо указать как вход, так и выход, поскольку этот тип может быть возвращен из одной функции и передан в другую.
Другой подход может быть:
func foo(_ param: Int) -> out Type { ... }
Цель состоит в том, чтобы избежать переноса и развертывания значений, использования дополнительных опций или других сложностей. В идеале вы могли бы напрямую получить из него примитивный тип, упрощая обработку нескольких возможных типов возвращаемых значений.
Таким образом (-> out Type) он может быть одним примитивным типом при передаче в другую функцию. Тип будет работать по тем же принципам, что и примитивные типы, но если вы все же хотите, чтобы тип имел несколько типов, используйте (_ param: in Type), но только два типа, а не Any.
Хорошее объяснение здесь: Обработка динамических выходных данных в функциях Swift без ущерба для безопасности типов
Изначально я использовал -> (Int, Int)? с nil, но я изменил его на текущую форму, потому что она определенно лучше и устраняет необходимость в дополнительных опциях. Теперь я перешел от работы с необязательным «уродством» к работе с любым «уродством».
Вы говорите о двух типах, но Bool — это просто жестко закодированное значение false, поэтому в основном это значение или всегда единственное постоянное значение false, которое возвращается. Так почему nil настолько хуже, чем false, я не понимаю. Вы могли бы избежать этого, выполнив функцию throw, но я предполагаю, что это тоже в некотором смысле некрасиво.
Вот хорошее объяснение этому stackoverflow.com/questions/78685976/…
Optional
определенно правильный путь сюда. Этот вариант использования — именно то, для чего он был разработан.
Кроме того, я не совсем понимаю, почему вы не используете Array
просто для Vector
.
Прежде всего, как вы, наверное, заметили, Swift, в отличие от Python, имеет строгую систему типов, включая типы, возвращаемые функциями, и реализовать то, что вы себе представляете, невозможно. Но у вас есть другие варианты.
Правильно ли я понимаю, что логический тип возвращаемого значения предназначен только для обозначения ошибки? Если да, то почему бы не выдать ошибку явно?
func vectorAssoc(_ v: Int, _ vec: Vector<(Int, Int)>) throws -> (Int, Int)
do {
print(try vectorAssoc(4, vec))
} catch {
print(false)
}
Если речь идет не об ошибках, а о возврате одной опции из ограниченного набора опций, в Swift для этой цели есть перечисления:
enum ReturnOption {
case bool(value: Bool)
case int(value: Int)
}
func vectorAssoc(_ v: Int, _ vec: Vector<(Int, Int)>) -> ReturnOption
switch vectorAssoc(4, vec) {
case .bool(let value),
.int(let value):
print(value)
}
Если вам нужны только два состояния: значение и отсутствующее значение, в Swift для этого есть Optional
:
func vectorAssoc(_ v: Int, _ vec: Vector<(Int, Int)>) -> (Int, Int)?
if let result = vectorAssoc(4, vec) {
print(result)
} else {
print(false)
}
Я бы сказал, что в этом случае возвращение необязательного параметра является правильным ответом.
Может быть, необязательно, что все делает автоматически.
Я добавил базовое объяснение, потому что первый вопрос был закрыт. Я не знаю, какое еще объяснение понадобится этому вопросу. Подход может быть чем-то похожим на python Int | Bool или, может быть, Any((Int, Int) Bool).