2015-10-21 10 views
9

Array von n Elementen gegeben, das heißtSchaltelemente in einem Array von Index

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

I eine Erweiterung der Array schreiben kann, so dass ich Array modifizieren Ausgabe zu erreichen: [2, 3, 4, 5, 1]:

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

Gibt es eine Möglichkeit, eine solche Funktion zu implementieren, die Array um jeden Index, positiv oder negativ, verschieben würde. Ich kann diese Funktion im imperativen Stil mit if-else Klauseln implementieren, aber was ich suche, ist die funktionale Implementierung.

Der Algorithmus ist einfach:

  1. Split-Array in zwei durch den Index bereitgestellt
  2. bis zum Ende der zweiten
  3. ersten Array append

Gibt es eine Möglichkeit, es in funktionellen umzusetzen Stil?

Der Code Ich habe mit fertig:

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]) 
    } 
} 
kann

Antwort

15

Sie reichten Subskribierung verwenden und die Ergebnisse verketten. Dies wird dir geben, was Sie suchen, mit Namen ähnlich wie die Standardbibliothek:

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] 

Statt Subskribierung Sie auch Array(suffix(count - amount) + prefix(amount)) von shiftRight() zurückkehren konnte.

+1

Große Lösung. Die Funktion, die ich beendet habe, ist ein bisschen anders, aber ich mag besonders, wie Sie Shift behandeln, wenn die Verschiebung negativ ist. –

+0

Ich habe den Code, mit dem ich fertig bin, zu meiner Frage hinzugefügt. Es ist ein bisschen anders, aber die Idee ist genau die gleiche. –

+0

Saubere Lösung! Ich würde den Funktionsgedanken umbenennen, da die Verschiebung _in place_ nicht stattfindet. Ich verstehe jedoch, dass dies seine Mutationseigenschaft implizieren soll. –

8

Mit Swift 3 können Sie shift(withDistance:) und shiftInPlace(withDistance:) Methoden mit nur wenigen Zeilen Code erstellen:

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) 
    } 

} 

Verbrauch:

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] 
0

Nach dem Nate Cook answers, ich brauche auch eine Reihe verschieben zurückkehrende Reihenfolge, so dass ich gemacht:

//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) 
    } 
} 

Wir haben zum Beispiel:

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] 
2

Ich nahm einen Stich beim Schreiben einige Erweiterungen dafür. Es hat einige nette Eigenschaften:

  • Verschiebung um einen Betrag größer als count verursacht einen Wrap-around.
  • Shifting durch negative Beträge dreht die Richtung
  • Funktionen wie die Bit-Verschiebung Binäroperatoren Exposes (<<, <<=, >>, >>=)


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) }*/ 

Sie es sehen können Aktion here.

Hier ist eine allgemeine Lösung, die diese Funktionalität träge für jede Art implementiert, das die Anforderungen erfüllt:

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) } 
} 
-1

in Objective C können Sie einfach so verschoben Array gelassen erhalten:

- (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; 
} 
+2

Die Frage war die Implementierung in Swift. Um NSMutableArray mit NSArray zu initialisieren, ist es möglich, '[Array MutableCopy];' Methode, es ist kürzer. –

+0

Richard, wenn Sie kopieren - Sie erhalten Kopie, durch Kopieren von Zeigern, nicht das neue Array. Also wird Ihre kürzere Methode die Logik durchbrechen;) –

-2

Der schnellste Weg ist (aber nimmt doppelten Speicher!):

Eingang:

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

Rotation LINKS:

var temp = arr 
    for i in 0..<n { 
     arr[(n-i+k)%n] = temp[i] 
    } 

result: [2, 1, 4, 3, 5] 

Rotation RECHTS:

var temp = arr 
    for i in 0..<n { 
    arr[(i+k)%n] = temp[i] 
    } 

result: [4, 1, 2, 3, 5] 
+0

Prinzip ist klar, aber Sie haben falschen Code zur Verfügung gestellt. Beide Sequenzen [2, 1, 4, 3, 5] und [4, 1, 2, 3, 5] sind keine Rotationen. – Alfishe

1

Hier ist eine funktionelle Implementierung für "in place" Drehung, die keine zusätzlichen Speicher benötigt noch eine temporäre Variable und führt nicht mehr als einen Austausch pro Element durch.

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 
     .reduce((i:0,r:count+rotations%count))    // i: swap index r:effective offset 
     { s,_ in let j = (s.i+s.r)%count      // j: index of value for position i 
     swap(&self[j],&self[s.i])       // swap to place value at rotated index 
     return (j,s.r)          // continue with next index to place 
     } 
    } 
} 

Es unterstützt optimal Null ist, positive und negative Drehungen sowie Drehungen der größeren Größe als die Größe des Arrays und Drehung einer Leer-Anordnung (das heißt, es kann nicht fehlschlagen).

Verwendet negative Werte, um in die andere Richtung zu rotieren (nach rechts).

Das Drehen eines 3-Elemente-Arrays um 10 ist wie das Drehen um 1, die ersten neun Drehungen bringen es in seinen ursprünglichen Zustand zurück (aber wir wollen Elemente nicht mehr als einmal bewegen).

Drehen eines 5-Elemente-Arrays um 3 nach rechts, d. H. RotateLeft (by: -3) entspricht rotateLeft (by: 2). Der "effektive Offset" der Funktion berücksichtigt dies.