Skip to content

Instantly share code, notes, and snippets.

@Tony-Y
Last active July 9, 2018 05:16
Show Gist options
  • Select an option

  • Save Tony-Y/40fa7fcc069ed5337e1a02c12d937287 to your computer and use it in GitHub Desktop.

Select an option

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

Tony-Y commented Jul 9, 2018

Copy link
Copy Markdown
Author

Stride Operators in Swift Forums.

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