Изменяемый указатель на строку в двумерном массиве

#arrays #swift #pointers

#массивы #swift #указатели

Вопрос:

Предположим, что существует двумерный массив

 var myArray: [[MyClass]]
  

Поскольку array является типом значения в Swift, присваивание типа

 let line = myArray[lineNumber]
  

создаст копию вместо ссылки на фактическую строку.

Чтобы изменять строки в массиве (например, вставлять / удалять элементы в строке) без копирования элементов и избегая чрезмерного использования индексации (т. Е. многократного использования myArray[lineNumber] в коде), я попробовал подход в стиле C с указателями.

Назначение

 let rowPointer = UnsafePointer(myArray[rowNumber])
  

похоже, что он работает не ожидаемым образом, ссылаясь на myArray[rowNumber][0] , а не на целую строку.

Однако,

 let rowPointer = UnsafePointer(myArray)   rowNumber
  

компилируется без ошибок и выполняет трюк, так что таким образом доступ ко всей строке может быть получен rowPointer.pointee

Поскольку мне также нужно изменить строку путем вставки / добавления элементов, я попробовал изменяемый указатель:

 let rowPointer = UnsafeMutablePointer(myArray)   rowNumber
  

Однако это присвоение приводит к ошибке компиляции:

Невозможно вызвать initializer for type 'UnsafeMutablePointer<_>' with an argument list of type '([[MyClass]])'

Есть ли способ получить доступ к строке с помощью изменяемого указателя?

Ответ №1:

Вот как вы можете изменить фактический указатель вашего массива, но я не уверен, имеет ли это какое-либо реальное значение по сравнению с существующей копией массива. Поскольку типы значений всегда копируются, поэтому мы получаем указатель, изменяем значение и присваиваем указателю новое значение.

Но лучше сделать это обычным способом, рассматривая array как тип значения. Кроме того, если ваш класс имеет ссылочный тип, это многое упрощает.

 var myArray: [[Int]] = [[1, 2, 3], [4, 5, 6]]

withUnsafeMutablePointer(to: amp;myArray) { pointer  in
    print(pointer)
    var pointee = pointer.pointee
    pointee[1][1] = 10
    pointer.pointee = pointee
}
  

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

1. Отлично! В моем случае мне нужно получить доступ к определенной строке. Поэтому я помещаю: withUnsafeMutablePointer(to: amp;myArray[номер строки]) {указатель на … } где (я надеюсь) pointer.pointee на самом деле является конкретной строкой в массиве, поэтому я использую pointer.pointee.insert(..) и pointer.pointee.append(…). У меня не было возможности посмотреть, как это работает, но должно быть все в порядке, учитывая, что он компилируется без ошибок.

Ответ №2:

Чего вы действительно пытаетесь достичь, так это добавить поддержку значений L. Значения, которые могут быть присвоены или отредактированы на месте. В Swift это достигается с помощью оператора подстрочного индекса:

 struct Matrix<T> {
    var rows: [[T]]

    init(rows: [[T]]) { self.rows = rows }

    init(width: Int, height: Int, initialElement: T) {
        let rows = (0..<height).map { _ in
            return Array(repeating: initialElement, count: width)
        }
        self.init(rows: rows)
    }

    subscript(row: Int, col: Int) -> T {
        get { return self.rows[row][col] }
        set { self.rows[row][col] = newValue }
    }
}

extension Matrix: ExpressibleByArrayLiteral {
    init(arrayLiteral: [T]...) {
        self.init(rows: arrayLiteral)
    }
}

extension Matrix: CustomDebugStringConvertible {
    var debugDescription: String {
        let height = rows.first?.count ?? 0
        let rowText = rows.map { row in
            row.map { element in String(describing: element) }.joined(separator: ", ")
        }.joined(separator: "],nt[")

        return """
            Matrix(width: (rows.count), height: (height), rows:
                [(rowText)]
            )
            """
    }
}

var matrix: Matrix = [
    ["A", "B"],
    ["C", "D"],
]

print(matrix)

matrix[0, 1] = "X"

print(matrix)
  

Я бы настоятельно рекомендовал это вместо подхода с небезопасным указателем, который может привести ко всевозможным проблемам с зависанием указателя или переполнением буфера. В конце концов, «Небезопасный» прямо там, в названии!

Ответ №3:

Вот еще одна простая версия для отображения матрицы с изменяемым [[Любым]].

   let row1: NSMutableArray = [1,"22",3]
  let row2: NSMutableArray = [2,"33",3]
  let row3: NSMutableArray = [4,"4",5]

  let matrix: [NSMutableArray] = [row1, row2, row3] // Here matrix is an immutable. This is the difference from other answers.

  matrix[0][1] =  22
  matrix[0][2] =  "23"
  matrix[0][3] =  "2333"

  print(type(of:matrix[0][1])) //  __NSCFNumber
  print(type(of:matrix[0][2])) // NSTaggedPointerString
  

Это более важно.

   let line1 = matrix[0] // here `let` again
  line1[2] = 33444

  print(matrix)  //matrix has changed. That means it is using reference. 
  

Надеюсь, это полезно.

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

1. Действительно, NSMutableArray является ссылочным типом, но он не является универсальным, поэтому у вас не может быть NSMutable массива с элементами определенного типа.

2. Он универсальный, поскольку использует любой nsobject. В нем просто отсутствует проверка типа. Но когда вы решаете использовать reference, вы в некотором роде рискуете. Этот сухой класс безопаснее, чем указатель.