-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathLuhnAlgoEager.swift
234 lines (201 loc) · 6.31 KB
/
LuhnAlgoEager.swift
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
234
// LuhnAlgoEager benchmark
//
// Description: Performs a Luhn checksum eagerly
// Source: https://gist.github.com/airspeedswift/e584239d7658b317f59a
import TestsUtils
public let benchmarks = [
BenchmarkInfo(
name: "LuhnAlgoEager",
runFunction: run_LuhnAlgoEager,
tags: [.algorithm]
),
]
@inline(never)
public func run_LuhnAlgoEager(_ n: Int) {
let resultRef = true
var result = false
for _ in 1...100*n {
result = eagerchecksum(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
}
// removing lazy at start of pipeline
// switches to array-based version
let eagerchecksum = { (ccnum: String) -> Bool in
ccnum //.lazy
|> { $0.reversed().lazy }
|> { mapSome($0, charToInt) }
|> { mapEveryN($0, 2, double) }
|> { map($0, combineDoubleDigits) }
|> sum
|> isMultipleOf(10)
}
let ccnum = "4012 8888 8888 1881"