Я перевел код с функционального языка программирования на Swift, и он хорошо работает при проверке через переменные. Однако при тестировании я столкнулся с ошибкой, связанной с Streamable и любой несовместимостью Streamable. Вот контекст:
Протокол Streamable действует как ограничение типа для дженериков или для объявления переменных, соответствующих протоколу. И наоборот, любой Streamable означает тип, соответствующий Streamable, используемый в замыканиях для захвата или возврата таких типов.
Как я могу использовать Streamable дляFunnyNumberStream?
Кстати, я сделал так, чтобы за ним было так просто следить, поэтому его не должно быть трудно понять.
protocol Streamable {
associatedtype Element
func next() -> (Element, Self)
}
struct OnesStream: Streamable {
func next() -> (Int, OnesStream) {
return (1, OnesStream())
}
}
struct LazyNaturalStream: Streamable {
private var current: Int
init(start: Int = 1) {
self.current = start
}
func next() -> (Int, LazyNaturalStream) {
let result = current
return (result, LazyNaturalStream(start: current + 1))
}
}
func streamForNSteps<S: Streamable>(_ s: S, _ n: Int) -> [S.Element] {
if n == 0 {
return []
} else {
let next = s.next()
return [next.0] + streamForNSteps(next.1, n - 1)
}
}
let funnyNumberStream: any Streamable = {
struct FunnyStream: Streamable {
var localLazyNaturalStream = LazyNaturalStream()
func next() -> (Int, FunnyStream) {
let currentValue = localLazyNaturalStream.next()
if currentValue.0 % 5 == 0 {
return (-currentValue.0, FunnyStream(localLazyNaturalStream: currentValue.1))
} else {
return (currentValue.0, FunnyStream(localLazyNaturalStream: currentValue.1))
}
}
}
return FunnyStream()
}()
// Example usage
// [1, 2, 3, 4, -5, 6, 7, 8, 9, -10, 11, 12, 13, 14, -15, 16]
let a = streamForNSteps(funnyNumberStream, 16)
Пример выше работает отлично, проблема в тестовых примерах.
Тестовые случаи:
final class hw4Test: XCTestCase {
func testFunnyNumberStream() throws {
// Act
let caseOneResult = streamForNSteps(funnyNumberStream, 0)
let caseTwoResult = streamForNSteps(funnyNumberStream, 16)
let caseThreeResult = streamForNSteps(funnyNumberStream, 5)
// Assert
XCTAssertEqual(caseOneResult, []) // Type 'Any' cannot conform to 'Equatable'
XCTAssertEqual(caseTwoResult, [1, 2, 3, 4, -5, 6, 7, 8, 9, -10, 11, 12, 13, 14, -15, 16]) // Type 'Any' cannot conform to 'Equatable'
XCTAssertEqual(caseThreeResult, [1, 2, 3, 4, -5]) // Type 'Any' cannot conform to 'Equatable'
}
}
Любые пояснения приветствуются!!!
let funnyNumberStream: any Streamable = { ... }
Это указывает на то, что funnyNumberStream
может быть реализован не только любым Streamable
типом, но и любым Streamable
типом для любого Element
типа. Кажется, вы имеете в виду, что это должен быть поток Int
. Это проще всего выразить с помощью первичного связанного типа:
protocol Streamable<Element> { // <--- Add <Element> as the primary associated type
associatedtype Element
func next() -> (Element, Self)
}
И тогда вы можете ограничить свою переменную Streamable
, которая возвращает Int
:
let funnyNumberStream: any Streamable<Int> = { ... }
Оно работает!!! Спасибо. Я до сих пор понятия не имею, что означает <Element> рядом с Streamable. Это какие-то дженерики? То есть вместо буквы вы использовали целое слово? Раньше я использовал один или два ассоциированных типа и давал полные имена, но не дженерики, что-то вроде нового.
Если я удалю связанный тип и оставлю только «обобщенный», я получу эту ошибку: связанный тип с именем «Элемент» должен быть объявлен в протоколе «Streamable» или протоколе, который он наследует.
В определении протокола <...>
указывает «основной связанный тип». Это относительно новая функция Swift (5.7). Его синтаксис похож на синтаксис дженериков, чтобы он выглядел немного более знакомым, но это совсем другое дело, и он вызывает определенные связанные типы для специальной обработки. Однако он не позволяет удалить связанный тип. Примерно до версии 5.7 то, что вы пытаетесь сделать здесь, было вообще невозможно, и вам пришлось бы создавать тип AnyStreamable вручную.
Полезно знать, кстати, есть ли способ сделать его равным для кортежа (-> любой Streamable<(Int, Int)>). Похоже, что текущий подход отлично работает с невложенными типами. Если я выбрасываю вложенные типы, я возвращаюсь к квадрату 1, где тестовые примеры не работают. Должен ли я создать еще один вопрос, аналогичный этому, для вложенных пользовательских типов и оставить его как есть (например, не вложенные вещи)? Я предполагаю, что это сложнее, чем просто расширение Equatable для кортежей...
Кортежи не могут соответствовать Equatable. Они не могут ничему соответствовать. Как правило, вам не следует использовать кортежи в Swift, за исключением крайне ограниченных, специальных ситуаций. Создайте структуру для вашего потока. stackoverflow.com/questions/54956028/tuple-vs-object-in-swift/…
Кстати, я добавил globalLazyNaturalStream в первый перевод просто для проверки. Обычно этот подход имеет немного другой результат в зависимости от того, как он реализован (как вызывается функция и ссылка). Теперь единственное, что мне нужно исправить, это любой Streamable, не беспокойтесь о других вещах. например, вывод нескольких случаев. Спасибо (я отредактирую, чтобы исправить код, без ответа)