Skip to content

Instantly share code, notes, and snippets.

@Tony-Y
Last active July 9, 2018 05:16
Show Gist options
  • Save Tony-Y/40fa7fcc069ed5337e1a02c12d937287 to your computer and use it in GitHub Desktop.
Save Tony-Y/40fa7fcc069ed5337e1a02c12d937287 to your computer and use it in GitHub Desktop.
ArraySliceWithStride: a[start .~ end .| step]
struct ArraySliceWithStride<Element> {
typealias Base = Array<Element>
private let _base: Base
private let _slice: Array<Any>.Slice
private var _start: Int { return _slice.start! }
let count: Int
private var _stride: Int { return _slice.stride }
init(_ base: Base, from start: Base.Index?, to end: Base.Index?, by stride: Base.Index.Stride) {
self._base = base
self._slice = Array.Slice(from: start, to: end, by: stride).relative(to: base)
self.count = self._slice.count
}
}
extension ArraySliceWithStride {
subscript (position: Int) -> Element {
get {
return _base[_start + position * _stride]
}
}
func iterator() -> AnyIterator<Element> {
var position = _start
return AnyIterator<Element>({
defer { position += self._stride }
return self._base[position]
})
}
func map<T>(_ transform: (Element) throws -> T) rethrows -> ArraySliceWithStride<T> {
var array = [T]()
array.reserveCapacity(count)
let iter = self.iterator()
for _ in 0..<count {
array.append(try transform(iter.next()!))
}
return array.allSliced()
}
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result {
var result = initialResult
let iter = self.iterator()
for _ in 0..<count {
result = try nextPartialResult(result, iter.next()!)
}
return result
}
func zip<T>(_ array2: ArraySliceWithStride<Element>, map transform: (Element, Element) throws -> T) rethrows -> ArraySliceWithStride<T> {
var array = [T]()
let minCount = min(self.count, array2.count)
array.reserveCapacity(minCount)
let iter1 = self.iterator()
let iter2 = array2.iterator()
for _ in 0..<minCount {
array.append(try transform(iter1.next()!, iter2.next()!))
}
return array.allSliced()
}
}
extension ArraySliceWithStride : CustomStringConvertible {
var description: String {
var text = ""
//text += "count: \(count) start: \(_start) stride: \(_stride)\n"
if count > 0 {
text += "[\(self[0])"
for i in 1..<count {
text += ", \(self[i])"
}
text += "]"
} else {
text += "[]"
}
return text
}
}
precedencegroup SliceFormationPrecedence {
associativity: left
lowerThan: AdditionPrecedence
}
infix operator .~: SliceFormationPrecedence
prefix operator .~
postfix operator .~
infix operator .|: SliceFormationPrecedence
prefix operator .|
func .~ (lhs: Int, rhs: Int) -> Array<Any>.Slice {
return Array.Slice(from: lhs, to: rhs, by: 1)
}
prefix func .~ (rhs: Int) -> Array<Any>.Slice {
return Array.Slice(from: nil, to: rhs, by: 1)
}
postfix func .~ (lhs: Int) -> Array<Any>.Slice {
return Array.Slice(from: lhs, to: nil, by: 1)
}
func .| (lhs: Array<Any>.Slice, rhs: Int) -> Array<Any>.Slice {
return Array.Slice(from: lhs.start, to: lhs.end, by: rhs * lhs.stride)
}
prefix func .| (rhs: Int) -> Array<Any>.Slice {
return Array.Slice(from: nil, to: nil, by: rhs)
}
extension Array {
struct Slice {
var start: Int?
var end: Int?
var stride: Int
init(from start: Int?, to end: Int?, by stride: Int) {
guard stride != 0 else {
fatalError("Stride size must not be zero")
}
self.start = start
self.end = end
self.stride = stride
}
var count: Int {
guard let start = start else { return 0 }
guard let end = end else { return 0 }
let offset = stride > 0 ? stride - 1 : stride + 1
return (end - start + offset) / stride
}
func relative(to array: Array<Element>) -> Array<Any>.Slice {
var start: Int
var end: Int
if self.stride > 0 {
start = self.start ?? array.startIndex
if start < 0 { start = array.endIndex + start }
if start < array.startIndex { start = array.startIndex }
end = self.end ?? array.endIndex
if end < 0 { end = array.endIndex + end }
if end > array.endIndex { end = array.endIndex }
if start > end {
fatalError("Start must not be more than End for positive Stride")
}
} else {
let arrayStart = array.endIndex.advanced(by: -1)
start = self.start ?? arrayStart
if start < 0 { start = array.endIndex + start }
if start > arrayStart { start = arrayStart }
let arrayEnd = array.startIndex.advanced(by: -1)
if let stop = self.end {
if stop < 0 { end = array.endIndex + stop } else { end = stop }
} else {
end = arrayEnd
}
if end < arrayEnd { end = arrayEnd }
if start < end {
fatalError("Start must not be less than End for negative Stride")
}
}
return Array<Any>.Slice(from: start, to: end, by: self.stride)
}
}
func sliced(from start: Index? = nil, to end: Index? = nil, by stride: Index.Stride) -> ArraySliceWithStride<Element> {
return ArraySliceWithStride(self, from: start, to: end, by: stride)
}
func allSliced() -> ArraySliceWithStride<Element> {
return ArraySliceWithStride(self, from: self.startIndex, to: self.endIndex, by: 1)
}
subscript (slice: Array<Any>.Slice) -> ArraySliceWithStride<Element> {
get {
return ArraySliceWithStride<Element>(self, from: slice.start, to: slice.end, by: slice.stride)
}
set {
let slice = slice.relative(to: self)
var position = slice.start!
let iter = newValue.iterator()
for _ in 0..<Swift.min(slice.count, newValue.count) {
self[position] = iter.next()!
position += slice.stride
}
}
}
}
// For example:
/*
var array1 = Array<Int32>(0..<10)
print(array1)
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let view1 = array1.sliced(from: 2, to: 10, by: 2)
print(view1)
// [2, 4, 6, 8]
print(view1.reduce(0, +))
// 20
let view2 = array1[2.~ .| 2]
print(view2)
// [2, 4, 6, 8]
let view3 = array1[(-1).~ .| -3]
print(view3)
// [9, 6, 3, 0]
print(array1[1.~ .| 2].zip(array1[2.~ .| 2], map: +))
// [3, 7, 11, 15]
array1[1.~ .| 2] = array1[2.~ .| 2].map({$0*$0})
print(array1)
// [0, 4, 2, 16, 4, 36, 6, 64, 8, 9]
*/

Comparison between Numpy and ArraySliceWithStride

NumPy ArraySliceWithStride Result
Array creation a = np.array(range(0,10)) var a = Array(0..<10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
All sliced                                           a[:]                     a[.|1]                               [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Sliced by 2                                         a[::2]                   a[.|2]                               [0, 2, 4, 6, 8]              
Sliced from 2 a[2:] a[2.~] [2, 3, 4, 5, 6, 7, 8, 9]
Sliced to the 2nd last index (-2) a[:-2] a[.~(-2)] [0, 1, 2, 3, 4, 5, 6, 7]
Sliced from 2 to -2 a[2:-2] a[2.~(-2)] [2, 3, 4, 5, 6, 7]
Sliced from 2 by 3 a[2::3] a[2.~ .| 3] [2, 5, 8]
Sliced to -2 by 3 a[:-2:3] a[.~(-2).|3] [0, 3, 6]
Sliced from 2 to -2 by 3 a[2:-2:3] a[2.~(-2).|3] [2, 5]
Storing the array sliced by 2 in the first 5 places a[:5] = a[::2] a[.~5] = a[.|2] [0, 2, 4, 6, 8, 5, 6, 7, 8, 9]
Element-wise addition a[::2] + a[1::2] a[.|2].zip(a[1.~ .| 2], map: +) [1, 5, 9, 13, 17]
Element-wise subtraction a[::2] - a[1::2] a[.|2].zip(a[1.~ .| 2], map: -) [-1, -1, -1, -1, -1]
Element-wise multiplication a[::2] * a[1::2] a[.|2].zip(a[1.~ .| 2], map: *) [0, 6, 20, 42, 72]
Element-wise division a[3::2] / a[2::2] a[3.~ .| 2].zip(a[2.~ .| 2], map: /) [1, 1, 1, 1]
Element-wise square a[::2]**2 a[.|2].map({$0*$0}) [0, 4, 16, 36, 64]
Maximum a[:-2:2].max() a[.~(-2).|2].reduce(Int.min, max) 6
Minimun a[2::2].min() a[2.~ .| 2].reduce(Int.max, min) 2
Sum a[2::2].sum() a[2.~ .| 2].reduce(0, +) 20
Product a[2::2].prod() a[2.~ .| 2].reduce(1, *) 384
@Tony-Y
Copy link
Author

Tony-Y commented Jul 9, 2018

Stride Operators in Swift Forums.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment