1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
// LuhnAlgoLazy benchmark
//
// Description: Performs a Luhn checksum lazily
// Source: https://gist.github.com/airspeedswift/e584239d7658b317f59a
import TestsUtils
public let benchmarks = [
BenchmarkInfo(
name: "LuhnAlgoLazy",
runFunction: run_LuhnAlgoLazy,
tags: [.algorithm]
),
]
@inline(never)
public func run_LuhnAlgoLazy(_ n: Int) {
let resultRef = true
var result = false
for _ in 1...100*n {
result = lazychecksum(ccnum)
if result != resultRef {
break
}
}
check(result == resultRef)
}
// Another version of the Luhn algorithm, similar to the one found here:
// https://gist.github.com/airspeedswift/b349c256e90da746b852
//
// This time, trying to keep two versions, one eager one lazy,
// as similar as possible. Only adding "lazy" to the start of
// the expression to switch between the two.
//
// Much of the same code as the previous version at the top,
// Skip down to line 110 for the different par
// mapSome is my Swift version of Haskell's mapMaybe, which
// is a map that takes a transform function that returns an
// optional, and returns a collection of only those values
// that weren't nil
// first we need a lazy view that holds the original
// sequence and the transform function
struct MapSomeSequenceView<Base: Sequence, T> {
fileprivate let _base: Base
fileprivate let _transform: (Base.Element) -> T?
}
// extend it to implement Sequence
extension MapSomeSequenceView: Sequence {
typealias Iterator = AnyIterator<T>
func makeIterator() -> Iterator {
var g = _base.makeIterator()
// AnyIterator is a helper that takes a
// closure and calls it to generate each
// element
return AnyIterator {
while let element = g.next() {
if let some = self._transform(element) {
return some
}
}
return nil
}
}
}
// now extend a lazy collection to return that view
// from a call to mapSome. In practice, when doing this,
// you should do it for all the lazy wrappers
// (i.e. random-access, forward and sequence)
extension LazyCollectionProtocol {
// I might be missing a trick with this super-ugly return type, is there a
// better way?
func mapSome<U>(
_ transform: @escaping (Elements.Element) -> U?
) -> LazySequence<MapSomeSequenceView<Elements, U>> {
return MapSomeSequenceView(_base: elements, _transform: transform).lazy
}
}
// curried function - call with 1 argument to get a function
// that tells you if i is a multiple of a given number
// e.g.
// let isEven = isMultipleOf(2)
// isEven(4) // true
func isMultipleOf<T: FixedWidthInteger>(_ of: T)->(T)->Bool {
return { $0 % of == 0 }
}
// extend LazySequence to map only every nth element, with all
// other elements untransformed.
extension LazySequenceProtocol {
func mapEveryN(
_ n: Int,
_ transform: @escaping (Element) -> Element
) -> LazyMapSequence<EnumeratedSequence<Self>, Element> {
let isNth = isMultipleOf(n)
func transform2(
_ pair: EnumeratedSequence<Self>.Element
) -> Element {
return isNth(pair.0 + 1) ? transform(pair.1) : pair.1
}
return self.enumerated().lazy.map(transform2)
}
}
infix operator |> : PipeRightPrecedence
precedencegroup PipeRightPrecedence {
associativity: left
}
func |><T,U>(t: T, f: (T)->U) -> U {
return f(t)
}
infix operator • : DotPrecedence
precedencegroup DotPrecedence {
associativity: left
}
func • <T, U, V> (g: @escaping (U) -> V, f: @escaping (T) -> U) -> (T) -> V {
return { x in g(f(x)) }
}
// function to free a method from the shackles
// of it's owner
func freeMemberFunc<T,U>(_ f: @escaping (T)->()->U)->(T)->U {
return { (t: T)->U in f(t)() }
}
extension String {
func toInt() -> Int? { return Int(self) }
}
// stringToInt can now be pipelined or composed
let stringToInt = freeMemberFunc(String.toInt)
// if only Character also had a toInt method
let charToString = { (c: Character) -> String in String(c) }
let charToInt = stringToInt • charToString
func sum<S: Sequence>(_ nums: S)->S.Element where S.Element: FixedWidthInteger {
return nums.reduce(0,+)
}
func reverse<C: LazyCollectionProtocol>(
_ source: C
) -> LazyCollection<ReversedCollection<C.Elements>> {
return source.elements.reversed().lazy
}
func map<S: LazySequenceProtocol, U>(
_ source: S, _ transform: @escaping (S.Elements.Element)->U
) -> LazyMapSequence<S.Elements,U> {
return source.map(transform)
}
func mapSome<C: LazyCollectionProtocol, U>(
_ source: C,
_ transform: @escaping (C.Elements.Element)->U?
) -> LazySequence<MapSomeSequenceView<C.Elements, U>> {
return source.mapSome(transform)
}
func mapEveryN<S: LazySequenceProtocol>(
_ source: S, _ n: Int,
_ transform: @escaping (S.Element)->S.Element
) -> LazyMapSequence<EnumeratedSequence<S>, S.Element> {
return source.mapEveryN(n, transform)
}
// Non-lazy version of mapSome:
func mapSome<S: Sequence, C: RangeReplaceableCollection>(
_ source: S,
_ transform: @escaping (S.Element)->C.Element?
) -> C {
var result = C()
for x in source {
if let y = transform(x) {
result.append(y)
}
}
return result
}
// Specialized default version of mapSome that returns an array, to avoid
// forcing the user having to specify:
func mapSome<S: Sequence,U>(
_ source: S,
_ transform: @escaping (S.Element
)->U?)->[U] {
// just calls the more generalized version
return mapSome(source, transform)
}
// Non-lazy version of mapEveryN:
func mapEveryN<S: Sequence>(
_ source: S, _ n: Int,
_ transform: @escaping (S.Element) -> S.Element
) -> [S.Element] {
let isNth = isMultipleOf(n)
return source.enumerated().map {
(pair: (offset: Int, element: S.Element)) in
isNth(pair.offset+1)
? transform(pair.element)
: pair.element
}
}
let double = { $0*2 }
let combineDoubleDigits = {
(10...18).contains($0) ? $0-9 : $0
}
// first, the lazy version of checksum calculation
let lazychecksum = { (ccnum: String) -> Bool in
ccnum.lazy
|> reverse
|> { mapSome($0, charToInt) }
|> { mapEveryN($0, 2, double) }
|> { map($0, combineDoubleDigits) }
|> sum
|> isMultipleOf(10)
}
let ccnum = "4012 8888 8888 1881"
|