Cambie los elementos en una matriz por índice

Dado un set de n elementos, es decir

var array = [1, 2, 3, 4, 5]

Puedo escribir una extensión en la Array para que pueda modificar la matriz para lograr este resultado: [2, 3, 4, 5, 1] :

  mutating func shiftRight() { append(removeFirst()) } 

¿Existe alguna forma de implementar una function que cambie la matriz por cualquier índice, positivo o negativo. Puedo implementar esta function en estilo imperativo con cláusulas if-else , pero lo que estoy buscando es implementación funcional.

El algorithm es simple:

  1. Dividir la matriz en dos según el índice proporcionado
  2. agregar la primera matriz al final del segundo

¿Hay alguna forma de implementarlo en el estilo funcional?

El código que he terminado con:

 extension Array { mutating func shift(var amount: Int) { guard -count...count ~= amount else { return } if amount < 0 { amount += count } self = Array(self[amount ..< count] + self[0 ..< amount]) } } 

Puede usar suscripciones a distancia y concatenar los resultados. Esto le dará lo que está buscando, con nombres similares a la biblioteca estándar:

 extension Array { func shiftRight(var amount: Int = 1) -> [Element] { assert(-count...count ~= amount, "Shift amount out of bounds") if amount < 0 { amount += count } // this needs to be >= 0 return Array(self[amount ..< count] + self[0 ..< amount]) } mutating func shiftRightInPlace(amount: Int = 1) { self = shiftRight(amount) } } Array(1...10).shiftRight() // [2, 3, 4, 5, 6, 7, 8, 9, 10, 1] Array(1...10).shiftRight(7) // [8, 9, 10, 1, 2, 3, 4, 5, 6, 7] 

En lugar de suscribirse, también puede devolver Array(suffix(count - amount) + prefix(amount)) de shiftRight() .

Con Swift 3, puede crear shift(withDistance:) y shiftInPlace(withDistance:) con solo unas pocas líneas de código:

 extension Array { func shift(withDistance distance: Int = 1) -> Array<Element> { let offsetIndex = distance >= 0 ? self.index(startIndex, offsetBy: distance, limitedBy: endIndex) : self.index(endIndex, offsetBy: distance, limitedBy: startIndex) guard let index = offsetIndex else { return self } return Array(self[index ..< endIndex] + self[startIndex ..< index]) } mutating func shiftInPlace(withDistance distance: Int = 1) { self = shift(withDistance: distance) } } 

Uso:

 let array1 = Array(1...10) let newArray = array1.shift(withDistance: 3) print(newArray) // prints: [4, 5, 6, 7, 8, 9, 10, 1, 2, 3] var array2 = Array(1...10) array2.shiftInPlace(withDistance: -2) print(array2) // prints: [9, 10, 1, 2, 3, 4, 5, 6, 7, 8] let array3 = Array(1...10) let newArray3 = array3.shift(withDistance: 30) print(newArray3) // prints: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] let array4 = Array(1...10) let newArray4 = array4.shift(withDistance: 0) print(newArray4) // prints: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] var array5 = Array(1...10) array5.shiftInPlace() print(array5) // prints: [2, 3, 4, 5, 6, 7, 8, 9, 10, 1] 

Me tomó la molestia de escribir algunas extensiones para esto. Tiene algunas características agradables:

  • Cambiar por una cantidad mayor que la count provoca un ajuste.
  • El desplazamiento por cantidades negativas voltea la dirección
  • Expone las funciones como los operadores binarys de bit-shift ( << , <<= , >> , >>= )

 extension Array { func shiftedLeft(by rawOffset: Int = 1) -> Array { let clampedAmount = rawOffset % count let offset = clampedAmount < 0 ? count + clampedAmount : clampedAmount return Array(self[offset ..< count] + self[0 ..< offset]) } func shiftedRight(by rawOffset: Int = 1) -> Array { return self.shiftedLeft(by: -rawOffset) } mutating func shiftLeft(by rawOffset: Int = 1) { self = self.shiftedLeft(by: rawOffset) } mutating func shiftRight(by rawOffset: Int = 1) { self = self.shiftedRight(by: rawOffset) } } //Swift 3 func << <T>(array: [T], offset: Int) -> [T] { return array.shiftedLeft(by: offset) } func >> <T>(array: [T], offset: Int) -> [T] { return array.shiftedRight(by: offset) } func <<= <T>(array: inout [T], offset: Int) { return array.shiftLeft(by: offset) } func >>= <T>(array: inout [T], offset: Int) { return array.shiftRight(by: offset) } /*// Swift 2.2 func << <T>(array: [T], offset: Int) -> [T] { return array.shiftedLeft(by: offset) } func >> <T>(array: [T], offset: Int) -> [T] { return array.shiftedRight(by: offset) } func <<= <T>(inout array: [T], offset: Int) { return array.shiftLeft(by: offset) } func >>= <T>(inout array: [T], offset: Int) { return array.shiftRight(by: offset) }*/ 

Puedes verlo en acción aquí .

Aquí hay una solución más general, que implementa esta funcionalidad perezosamente para cualquier tipo que cumpla con los requisitos:

 extension RandomAccessCollection where Self: RangeReplaceableCollection, Self.Index == Int, Self.IndexDistance == Int { func shiftedLeft(by rawOffset: Int = 1) -> RangeReplaceableSlice<Self> { let clampedAmount = rawOffset % count let offset = clampedAmount < 0 ? count + clampedAmount : clampedAmount return self[offset ..< count] + self[0 ..< offset] } func shiftedRight(by rawOffset: Int = 1) -> RangeReplaceableSlice<Self> { return self.shiftedLeft(by: -rawOffset) } mutating func shiftLeft(by rawOffset: Int = 1) { self = Self.init(self.shiftedLeft(by: rawOffset)) } mutating func shiftRight(by rawOffset: Int = 1) { self = Self.init(self.shiftedRight(by: rawOffset)) } //Swift 3 static func << (c: Self, offset: Int) -> RangeReplaceableSlice<Self> { return c.shiftedLeft(by: offset) } static func >> (c: Self, offset: Int) -> RangeReplaceableSlice<Self> { return c.shiftedRight(by: offset) } static func <<= (c: inout Self, offset: Int) { return c.shiftLeft(by: offset) } static func >>= (c: inout Self, offset: Int) { return c.shiftRight(by: offset) } } 

Siguiendo las respuestas de Nate Cook , también necesito cambiar una matriz que devuelve el order inverso, así que hice:

 //MARK: - Array extension Array { func shiftRight( amount: Int = 1) -> [Element] { var amountMutable = amount assert(-count...count ~= amountMutable, "Shift amount out of bounds") if amountMutable < 0 { amountMutable += count } // this needs to be >= 0 return Array(self[amountMutable ..< count] + self[0 ..< amountMutable]) } func reverseShift( amount: Int = 1) -> [Element] { var amountMutable = amount amountMutable = count-amountMutable-1 let a: [Element] = self.reverse() return a.shiftRight(amountMutable) } mutating func shiftRightInPlace(amount: Int = 1) { self = shiftRight(amount) } mutating func reverseShiftInPlace(amount: Int = 1) { self = reverseShift(amount) } } 

Tenemos, por ejemplo:

 Array(1...10).shiftRight() // [2, 3, 4, 5, 6, 7, 8, 9, 10, 1] Array(1...10).shiftRight(7) // [8, 9, 10, 1, 2, 3, 4, 5, 6, 7] Array(1...10).reverseShift() // [2, 1, 10, 9, 8, 7, 6, 5, 4, 3] Array(1...10).reverseShift(7) // [8, 7, 6, 5, 4, 3, 2, 1, 10, 9] 

Aquí hay una implementación funcional para la rotation "in situ" que no requiere memory adicional ni una variable temporal y no realiza más de un intercambio por elemento.

 extension Array { mutating func rotateLeft(by rotations:Int) { let _ = // silence warnings (1..<Swift.max(1,count*((rotations+1)%(count+1)%1))) // will do zero or count - 1 swaps .networkinguce((i:0,r:count+rotations%count)) // i: swap index r:effective offset { s,_ in let j = (s.i+sr)%count // j: index of value for position i swap(&self[j],&self[si]) // swap to place value at rotated index return (j,sr) // continue with next index to place } } } 

Optimiza de manera óptima las rotaciones cero, positiva y negativa, así como las rotaciones de mayor magnitud que el tamaño de la matriz y la rotation de una matriz vacía (es decir, no puede fallar).

Utiliza valores negativos para girar en la otra dirección (a la derecha).

Rotar un set de 3 elementos por 10 es como rotarlo en 1, las primeras nueve rotaciones lo devolverán a su estado inicial (pero no queremos mover elementos más de una vez).

Girar una matriz de 5 elementos a la derecha por 3, es decir, rotar Leve (por: -3) es equivalente a rotar Leve (por: 2). El "desplazamiento efectivo" de la function lo tiene en count.

En el objective C, simplemente puede get una matriz desplazada a la izquierda como esta:

 - (NSMutableArray *)shiftedArrayWithOffset:(NSInteger)offset { NSMutableArray *bufferArray = [[NSMutableArray alloc] initWithArray:originalArray]; for (int i = 0; i < offset; i++) { id object = [bufferArray firstObject]; [bufferArray removeObjectAtIndex:0]; [bufferArray addObject:object]; } return bufferArray; } 

La forma más rápida es (¡pero lleva doble memory!):

input:

 var arr = [1,2,3,4,5] let k = 1 (num steps to rotate) let n = arr.count ( a little but faster ) 

rotation IZQUIERDA :

  var temp = arr for i in 0..<n { arr[(n-i+k)%n] = temp[i] } result: [2, 1, 4, 3, 5] 

rotation DERECHA :

  var temp = arr for i in 0..<n { arr[(i+k)%n] = temp[i] } result: [4, 1, 2, 3, 5]