Неявно развернутое необязательное странное поведение с картой

#swift #optional

#swift #тип параметра

Вопрос:

Что я обнаружил

Этот вопрос касается чего-то, что я заметил немного странным в Swift языке. Я столкнулся с этим поведением, поскольку это была ошибка в моем коде.

Если я создал массив для сетки как неявно развернутый необязательный элемент, то map ведет себя странно. Взгляните на этот код:

 let grid: [[Int]]! = [ // May be defined like this if set later on
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]


print(grid.map { $0[0] }!)
// Prints "[1, 2, 3]" (not what I wanted)

print(grid!.map { $0[0] })
// Prints "[1, 4, 7]" (what I wanted)
  

Я знаю, что строку можно получить, просто выполнив grid[0] . Тем не менее, я пытаюсь получить столбец.

Я попробовал первый метод, описанный выше, который выдает только строку вместо столбца. Второй метод сработал и выдал столбец.

В чем причина?

Я определил grid как [[Int]]! , что является неявно развернутым необязательным.

  • Почему мне нужно заставить unwrap grid правильно использовать map ?
  • Почему первый метод действует так же, как возврат строки с помощью grid[0] ?

Комментарии:

1. Должно ли это быть [[Int]]! вместо [[Int]] ?

2. @AhmadF Это определяется как [[Int]]! . Это связано с тем, что, как говорится в комментарии, оно может быть установлено позже. Оно могло быть объявлено как var grid: [[Int]]! , а затем установлено позже.

3. Помимо вашего основного вопроса, я предлагаю вам выделить этот шаблон «настраиваемый, но только один раз» в отдельный тип, чтобы вы могли «убрать» необязательность, forums.swift.org/t /…

4. @Alexander Я, честно говоря, не думаю, что для этого стоит использовать дополнительный код. Я буду придерживаться just var , это намного аккуратнее.

5. @George_E Это зависит от того, как часто вы это делаете. Как правило, шаблон «установить обязательный набор хотя бы один раз» довольно распространен. При 2 вариантах использования я бы рассмотрел это, при 3 я бы настоятельно рекомендовал это. Строки кода не определяют сложность. В большинстве случаев «более простое» решение на самом деле длиннее, потому что оно хорошо справляется с абстрагированием деталей.

Ответ №1:

Существует две версии map : одна, которая работает с массивом, и другая, которая работает с опциями.

Ваш grid является необязательным, и хотя это неявно развернутый необязательный параметр (IUO), он все еще является необязательным. SWIFT будет лечить ИУО как необязательно всякий раз, когда это возможно, и только силой разворачивает значение для развернутого типа не требуется.

Из-за этого вместо ожидаемой используется необязательная версия map . Явно разворачивая grid с ! помощью, вы затем разрешаете использовать желаемую версию map .

Как map работает с необязательным?

Когда карта применяется к необязательному объекту, она применяет закрытие к развернутому значению. Если необязательным является nil , ничего не происходит.

Итак, grid разворачивается и становится $0 , и закрытие возвращает $0[0] , которое является первой строкой grid .


Примечание: В Xcode, если вы optionнажмете map на каждое утверждение, вы увидите, что первое говорит:

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

и второй:

Возвращает массив, содержащий результаты сопоставления данного замыкания по элементам последовательности.

Комментарии:

1. Пожалуйста. Я думаю, что разработчики Swift могли бы избежать этой путаницы, назвав map , который работает с опциями, чем-то другим.

2. Да, мне потребовалась целая вечность, чтобы выяснить ошибку, поскольку я делал решатель судоку. Он проверял только строки, так как столбцы не проверялись 😛 Его следует переименовать в mapOpt или что-то в этом роде.

3. Я думаю, что оно названо map потому, что и optional, и array являются функторами, и map это общее название для функторов в функциональных языках ( en.wikipedia.org/wiki/Functor_ (functional_programming) )