Как я могу создать индекс с геттером, который возвращает необязательный, но сеттер, который возвращает необязательный

Я пишу собственный сборник под названием TriangularArray<T>. Он представляет собой такую ​​структуру:

     x
    x x
   x x x
  x x x x
 x x x x x

где каждый x является элементом массива. Я могу получить доступ к элементам с помощью номера строки и номера индекса, начиная с нуля. Например, доступ к (4, 2) из ​​следующего:

     a
    b c
   d e f
  g h i j
 k l m n o

приведет к m (5-я строка, третье значение в этой строке).

Я использовал [[T]] в качестве массива поддержки и написал такой нижний индекс:

subscript(_ row: Int, _ index: Int) -> T? {
    get {
        // innerArray is the [[T]] used for backing
        if row < 0 || row >= innerArray.count {
            return nil
        }
        if index < 0 || index > row {
            return nil
        }
        return innerArray[row][index]
    }

    set {
        if row < 0 || row >= innerArray.count {
            return
        }
        if index < 0 || index > row {
            return
        }
        innerArray[row][index] = newValue!
    }
}

Логика заключается в том, что индекс вернет nil, если вы получите доступ к несуществующей строке и индексу, например (1, 3). Однако, когда индекс возвращает T?, newValue в установщике также становится необязательным, и мне нужно принудительно его развернуть.

Я действительно хочу проверить такие вещи во время компиляции:

triangularArray[0, 0] = nil // this should be a compile time error

Я попытался найти это в Google, но нашел только этот вопрос, который очень устарел. Конечно, мы можем добиться большего в Swift 4.2, не так ли?

Не думаю, что ситуация изменилась. Геттеры / сеттеры подстрочного индекса по-прежнему возвращают / принимают тот же тип.

Martin R 27.10.2018 13:15

@MartinR Понятно. Может быть, я смогу использовать какую-нибудь другую языковую функцию для достижения подобной эстетики и в то же время запретить nil ...

Sweeper 27.10.2018 13:18

Единственное, что приходит мне в голову, - это определить два метода индексации: один для получения / установки необязательного значения, а другой индекс только для получения, возвращающий необязательное значение (например, «безопасный метод доступа к массиву», обсуждаемый в настоящее время на forum.swift.org/t/add-accessor-with-bounds-check-to-array/…‌)

Martin R 27.10.2018 13:41

В самом деле, стандартная библиотека обрабатывает это, делая такие дополнительные возвращающие средства доступа доступными только для получения, например свойства (Bidirectional)Collectionfirst и last.

Hamish 27.10.2018 14:11

Другой вариант (также из обсуждения на форуме Swift) - определить метод доступа elementAt(row:index:) -> T?. Другой вопрос, действительно ли вы хотите, чтобы сеттер игнорировал недействительные индексы без уведомления, или это должно привести к сбою.

Martin R 28.10.2018 15:40

С помощью этого кода вы не можете добавить новый линии в innerArray. Это желаемое поведение?

ielyamani 03.11.2018 14:47

@ Carpsen90 в коллекции всегда будет фиксированное количество строк, так что да.

Sweeper 03.11.2018 14:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
7
270
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Только Swift 4.2 может помочь вам отметить такие проблемы в нашем коде, я не думаю, что это возможно с 4.1 или ниже.

Пример Swift 4.2:

#if true
   #error("add here your compiler error")
#endif
Ответ принят как подходящий

К сожалению, это невозможно сделать со Swift (и другими языками, работающими с динамическими массивами) по той причине, что размер массива является динамическим и не известен во время компиляции (он может быть инициализирован любым значением во время работы программы).
Например, если TriangularArray<T> имеет размер 1, то [0, 0]является допустимым элементом, и компилятор не может знать это заранее. В стандартной библиотеке нет ошибки времени компиляции, когда вы пытаетесь получить доступ к массиву, подобному array[-1] - это всегда будет компилироваться, но всегда будет приводить к ошибке времени выполнения.

Я думаю, что решение, которое у вас есть в настоящее время с использованием дополнительных опций, кажется здесь лучшим сценарием. Вы также должны быть последовательны в том, как работает Array, и запускать fatalError, если указаны недопустимые индексы. Альтернативой было бы создание пользовательской структуры Index в TriangularArray, которая представляет индексы треугольника и может быть дополнительно построена из значений x и y (хотя это может немного усложнить ситуацию).

PS: Этот ответ предполагает, что TriangularArray<T> может иметь треугольник произвольной высоты (высоту, которую можно указать во время выполнения), поскольку это не указано в вопросе. Если высота определяется во время компиляции, тогда границы могут быть жестко закодированы и, как упомянул @Raul Mantilla, можно использовать #error.

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