Last active
April 7, 2016 11:19
-
-
Save beccadax/b24dd89a770d9fe376984498d3185187 to your computer and use it in GitHub Desktop.
Suggested replacement for C-style for.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Sequence of 1, 2, 4 ... 64 | |
induce(from: 1, to: 128, by: { $0 * 2 }) | |
induce(from: 1, to: 128, by: { $0 *= 2 }) | |
// Sequence of 1, 2, 4 ... 128 | |
induce(from: 1, through: 128, by: { $0 * 2 }) | |
induce(from: 1, through: 128, by: { $0 *= 2 }) | |
// Sequence of 1, 2, 4 ... with arbitrary, calculated bound | |
import Darwin | |
induce(from: 1, while: { sqrt(Double($0)) < 20 }, by: { $0 * 2 }) | |
induce(from: 1, while: { sqrt(Double($0)) < 20 }, by: { $0 *= 2 }) | |
// Sequence of two values in a tuple: | |
induce(from: (1, 10), while: { $0.0 < 128 }, by: { $0.0 *= 2; $0.1 *= 2 }) | |
// C-style for loop: | |
for var i = 1; i < 128; i *= 2 { print(i) } | |
// Literal converted form: | |
for i in induce(from: 1, while: { $0 < 128 }, by: { $0 *= 2 }) { print(i) } | |
// Idomatic converted form: | |
for i in induce(from: 1, to: 128, by: { $0 * 2 }) { print(i) } | |
// It would be really nice if you could say something like one of these: | |
// for i in induce(from: 1, to: 128, by: * 2) { print(i) } | |
// for i in induce(from: 1, to: 128, by: _ * 2) { print(i) } | |
// for i in induce(from: 1, to: 128, by: i * 2) { print(i) } | |
// But that kind of thing has been rejected before. Sigh... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file is not optimized or stdlib-ready. It's merely meant to demonstrate the logic the various | |
// `induce` functions are intended to implement. | |
/// Returns a lazy sequence which begins with the `start` element, then derives subsequent elements | |
/// by repeatedly calling the `next` function with the previous element to calculate the next element. | |
/// Continues as long as the `inBounds` function continues to return `true` for each element. | |
/// | |
/// If `inBounds` is omitted, the sequence created by this function is infinite. | |
public func induce<Element>(from start: Element, while inBounds: Element -> Bool = { _ in true }, by next: Element -> Element) -> InductionSequence<Element> { | |
return induce(from: start, while: inBounds, by: formify(next)) | |
} | |
/// Returns a lazy sequence which begins with the `start` element, then derives subsequent elements | |
/// by repeatedly calling the `formNext` function to transform the previous element into the next one. | |
/// Continues as long as the `inBounds` function continues to return `true` for each element. | |
/// | |
/// If `inBounds` is omitted, the sequence created by this function is infinite. | |
public func induce<Element>(from start: Element, while inBounds: Element -> Bool = { _ in true }, by formNext: inout Element -> Void) -> InductionSequence<Element> { | |
return InductionSequence(start: start, inBounds: inBounds, formNext: formNext) | |
} | |
/// Returns a lazy sequence which begins with the `start` element, then derives subsequent elements | |
/// by repeatedly calling the `next` function with the previous element to calculate the next element. | |
/// Continues as long as the elements stay between `start` (inclusive) and `end` (exclusive). | |
func induce<Element: Comparable>(from start: Element, to end: Element, by next: Element -> Element) -> InductionSequence<Element> { | |
return induce(from: start, to: end, by: formify(next)) | |
} | |
/// Returns a lazy sequence which begins with the `start` element, then derives subsequent elements | |
/// by repeatedly calling the `formNext` function to transform the previous element into the next one. | |
/// Continues as long as the elements stay between `start` (inclusive) and `end` (exclusive). | |
func induce<Element: Comparable>(from start: Element, to end: Element, by formNext: inout Element -> Void) -> InductionSequence<Element> { | |
let inBounds = (start < end) ? { $0 < end } : { $0 > end } | |
return induce(from: start, while: inBounds, by: formNext) | |
} | |
/// Returns a lazy sequence which begins with the `start` element, then derives subsequent elements | |
/// by repeatedly calling the `next` function with the previous element to calculate the next element. | |
/// Continues as long as the elements stay between `start` (inclusive) and `end` (inclusive). | |
func induce<Element: Comparable>(from start: Element, through end: Element, by next: Element -> Element) -> InductionSequence<Element> { | |
return induce(from: start, through: end, by: formify(next)) | |
} | |
/// Returns a lazy sequence which begins with the `start` element, then derives subsequent elements | |
/// by repeatedly calling the `formNext` function to transform the previous element into the next one. | |
/// Continues as long as the elements stay between `start` (inclusive) and `end` (inclusive). | |
func induce<Element: Comparable>(from start: Element, through end: Element, by formNext: inout Element -> Void) -> InductionSequence<Element> { | |
let inBounds = (start < end) ? { $0 <= end } : { $0 >= end } | |
return induce(from: start, while: inBounds, by: formNext) | |
} | |
/// A lazy, possibly infinite sequence derived from a starting element and a function which calculates | |
/// the next value. | |
/// | |
/// -SeeAlso: `induce(from:to:by:)`, `induce(from:through:by:)`, `induce(from:while:by:)` | |
public struct InductionSequence<Element>: SequenceType { | |
let start: Element | |
let inBounds: Element -> Bool | |
let formNext: inout Element -> Void | |
public func generate() -> InductionGenerator<Element> { | |
return .init(current: start, inBounds: inBounds, formNext: formNext) | |
} | |
} | |
/// A lazy, possibly infinite generator associated with an `InductionSequence`. | |
public struct InductionGenerator<Element>: GeneratorType { | |
var current: Element | |
let inBounds: Element -> Bool | |
let formNext: inout Element -> Void | |
public mutating func next() -> Element? { | |
if !inBounds(current) { | |
return nil | |
} | |
defer { formNext(¤t) } | |
return current | |
} | |
} | |
/// Converts a functional-style operation into a mutating-style operation. | |
private func formify<T>(function: T -> T) -> (inout T) -> Void { | |
return { $0 = function($0) } | |
} |
@brentdax Heh, likewise, I like the functionality of induce
but not the name. You could even call it iterate
, and simply make the while:
-less overload work the way I suggested.
- True.
- That's an interesting point. I'm not sure if it's that indirect if merely the order of arguments changes in the expression.
- Naming is hard. I think some alternatives were discussed:
xs.prefix {...}
or evenxs.while {...}
could work as well.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@BurntCaramel Sort of, yes. There are a couple of issues with it: it doesn't support the more flexible
while:
variant, and the "walk from end to start based on the sign ofby:
" trick doesn't work with aby:
function. There are ways to make it work, but they're a little more painful. I would probably provide these variants:@pyrtsa
takeWhile
would obviate the need forwhile:
. I didn't use it here for three reasons:induce(from: expr, while: function, by: function)
closely matches the C-style for loop, and I thought a direct translation might be valuable.takeWhile
, but not the name.Even if we do have a
takeWhile
, though, I think we would wantfrom:to/through:
or an equivalent range-based version. The ability to declare the boundary values of an iteration instead of specifying a cutoff function is a win over C-stylefor
, and I would like to make that available to users even when theby:
is not something thatstride
can do.