Использование протокола как типа должно быть записано с любыми (целями обучения)

Я перевел код с функционального языка программирования на 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'
    }
}

Любые пояснения приветствуются!!!

Кстати, я добавил globalLazyNaturalStream в первый перевод просто для проверки. Обычно этот подход имеет немного другой результат в зависимости от того, как он реализован (как вызывается функция и ссылка). Теперь единственное, что мне нужно исправить, это любой Streamable, не беспокойтесь о других вещах. например, вывод нескольких случаев. Спасибо (я отредактирую, чтобы исправить код, без ответа)

Adrian 27.06.2024 20:02
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
98
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
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. Это какие-то дженерики? То есть вместо буквы вы использовали целое слово? Раньше я использовал один или два ассоциированных типа и давал полные имена, но не дженерики, что-то вроде нового.

Adrian 28.06.2024 11:20

Если я удалю связанный тип и оставлю только «обобщенный», я получу эту ошибку: связанный тип с именем «Элемент» должен быть объявлен в протоколе «Streamable» или протоколе, который он наследует.

Adrian 28.06.2024 11:28

В определении протокола <...> указывает «основной связанный тип». Это относительно новая функция Swift (5.7). Его синтаксис похож на синтаксис дженериков, чтобы он выглядел немного более знакомым, но это совсем другое дело, и он вызывает определенные связанные типы для специальной обработки. Однако он не позволяет удалить связанный тип. Примерно до версии 5.7 то, что вы пытаетесь сделать здесь, было вообще невозможно, и вам пришлось бы создавать тип AnyStreamable вручную.

Rob Napier 28.06.2024 14:03

Полезно знать, кстати, есть ли способ сделать его равным для кортежа (-> любой Streamable<(Int, Int)>). Похоже, что текущий подход отлично работает с невложенными типами. Если я выбрасываю вложенные типы, я возвращаюсь к квадрату 1, где тестовые примеры не работают. Должен ли я создать еще один вопрос, аналогичный этому, для вложенных пользовательских типов и оставить его как есть (например, не вложенные вещи)? Я предполагаю, что это сложнее, чем просто расширение Equatable для кортежей...

Adrian 28.06.2024 17:16

Кортежи не могут соответствовать Equatable. Они не могут ничему соответствовать. Как правило, вам не следует использовать кортежи в Swift, за исключением крайне ограниченных, специальных ситуаций. Создайте структуру для вашего потока. stackoverflow.com/questions/54956028/tuple-vs-object-in-swif‌​t/…

Rob Napier 28.06.2024 22:53

Другие вопросы по теме

Какой смысл имеет оператор стрелки в Haskell в функциях, принимающих более одного параметра?
Как исправить столбец с числовыми значениями, который воспринимается как строковое поле из-за пустых строк в фрейме данных Pandas?
Как создать собственный тип для анализа [u8;32] из json, содержащего шестнадцатеричную строку, в Rust
Ограничить тип возвращаемого значения подтипом этого
Компиляция карты времени имени строки типа в типы в C++
Вычисление чисел, для которых требуется тип данных размером более 16 байт в C++
Введите 'строка | логическое значение не может быть назначено для ввода «никогда» в машинописном тексте
Почему тип int меняет размер в зависимости от архитектуры процессора, а другие типы — нет?
Определение макроса и использование типов данных для поиска абсолютного значения
Аргумент func с массивом смешанных типов (протокол), затем вызовите статический метод протокола