<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Following up to myself. Thoughts and feedback are welcome. -- Erica<div class=""><br class=""></div><div class=""><h1 id="changingthebehaviorofstridethroughgenerator" style="margin: 0px 0px 0.875em; font-size: 1.5em; line-height: 0.875em; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Changing the Behavior of StrideThroughGenerator</h1><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">Swift offers two stride functions, <code class="">stride(to:, by:)</code> and <code class="">stride(through:, by:)</code>. I propose to change the way the <code class="">through</code> variation works. </p><h2 id="currentart" style="color: rgb(17, 17, 17); margin: 0px 0px 0.9545454545454546em; font-size: 1.375em; line-height: 0.9545454545454546em; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Current Art</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">A <code class="">Strideable to</code> sequence returns the sequence of values (<code class="">self</code>, <code class="">self + stride</code>, <code class="">self + stride + stride</code>, … <em class="">last</em>) where <em class="">last</em> is the last value in<br class="">the progression that is less than <code class="">end</code>. </p><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">A <code class="">Strideable through</code> sequence currently returns the sequence of values (<code class="">self</code>, <code class="">self + stride</code>, <code class="">self + tride + stride</code>, … <em class="">last</em>) where <em class="">last</em> is the last value in the progression less than or equal to <code class="">end</code>. There is no guarantee that <code class="">end</code> is an element of the sequence. </p><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">Under the current implementation, each floating point addition accrues errors. The progression never reaches 2.0. </p><pre style="color: rgb(17, 17, 17); font-size: 16px;" class=""><code class=" lasso hljs" style="display: block; padding: 0.5em; color: rgb(51, 51, 51); background-color: rgb(248, 248, 248); background-position: initial initial; background-repeat: initial initial;">print(<span class="hljs-built_in" style="color: rgb(0, 134, 179);">Array</span>(<span class="hljs-number" style="color: rgb(0, 153, 153);">1.0</span><span class="hljs-built_in" style="color: rgb(0, 134, 179);">.</span>stride(through: <span class="hljs-number" style="color: rgb(0, 153, 153);">2.0</span>, <span class="hljs-keyword" style="font-weight: bold;">by</span>: <span class="hljs-number" style="color: rgb(0, 153, 153);">0.1</span>)))
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">/// Prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]</span></code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">To force the progression to include 2.0, you must add an (ugly) epsilon, as in the following example:</p><pre style="color: rgb(17, 17, 17); font-size: 16px;" class=""><code class=" lasso hljs" style="display: block; padding: 0.5em; color: rgb(51, 51, 51); background-color: rgb(248, 248, 248); background-position: initial initial; background-repeat: initial initial;">print(<span class="hljs-built_in" style="color: rgb(0, 134, 179);">Array</span>(<span class="hljs-number" style="color: rgb(0, 153, 153);">1.0</span><span class="hljs-built_in" style="color: rgb(0, 134, 179);">.</span>stride(through: <span class="hljs-number" style="color: rgb(0, 153, 153);">2.01</span>, <span class="hljs-keyword" style="font-weight: bold;">by</span>: <span class="hljs-number" style="color: rgb(0, 153, 153);">0.1</span>)))
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">/// Prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]</span></code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">This is problematic for the following reasons: </p><ul style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; font-size: 16px;" class=""><li style="font-size: 18px;" class="">The name of the calling function “through” suggests the progression will pass <em class="">through</em> the end point before stopping</li><li style="font-size: 18px;" class="">Floating point calls present an extremely common use-case</li><li style="font-size: 18px;" class="">It’s unreasonable to expect developers to consider every case of “will floating point math prevent my progression from actually reaching the end point, which has already been differentiated by using <code class="">through</code> rather than <code class="">to</code>”</li></ul><h2 id="proposedmodifications" style="color: rgb(17, 17, 17); margin: 0px 0px 0.9545454545454546em; font-size: 1.375em; line-height: 0.9545454545454546em; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Proposed Modifications</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">I recommend the following changes: </p><h4 id="changethedocumentationtextfrom" style="color: rgb(17, 17, 17); margin: 0px 0px 1.1666666666666667em; font-size: 1.125em; line-height: 1.1666666666666667em; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Change the documentation text from</h4><blockquote style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; font-size: 16px;" class=""><p style="word-wrap: break-word; font-size: 18px; line-height: 1.6em; margin: 1.3125em 0px; font-style: italic;" class="">A <code class="">Strideable through</code> sequence currently returns the sequence of values (<code class="">self</code>, <code class="">self + stride</code>, <code class="">self + stride + stride</code>, … <em class="">last</em>) where <em class="">last</em> is the last value in the progression <strong class="">less than or equal to</strong> <code class="">end</code>. There is no guarantee that <code class="">end</code> is an element of the sequence. </p></blockquote><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">to </p><blockquote style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; font-size: 16px;" class=""><p style="word-wrap: break-word; font-size: 18px; line-height: 1.6em; margin: 1.3125em 0px; font-style: italic;" class="">A <code class="">Strideable through</code> sequence currently returns the sequence of values (<code class="">self</code>, <code class="">self + stride</code>, <code class="">self + stride + stride</code>, … <em class="">last</em>) where <em class="">last</em> is the last value in the progression <strong class="">greater than or equal to</strong> <code class="">end</code>. There is no guarantee that <code class="">end</code> is an element of the sequence. </p></blockquote><h4 id="modifytheimplementation" style="color: rgb(17, 17, 17); margin: 0px 0px 1.1666666666666667em; font-size: 1.125em; line-height: 1.1666666666666667em; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Modify the implementation</h4><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">Current:</p><pre style="color: rgb(17, 17, 17); font-size: 16px;" class=""><code class="swift ruby hljs" style="display: block; padding: 0.5em; color: rgb(51, 51, 51); background-color: rgb(248, 248, 248); background-position: initial initial; background-repeat: initial initial;"> /<span class="hljs-regexp" style="color: rgb(0, 153, 38);">//</span> <span class="hljs-constant" style="color: rgb(0, 153, 153);">Advance</span> to the <span class="hljs-keyword" style="font-weight: bold;">next</span> element <span class="hljs-keyword" style="font-weight: bold;">and</span> <span class="hljs-keyword" style="font-weight: bold;">return</span> it, <span class="hljs-keyword" style="font-weight: bold;">or</span> `<span class="hljs-keyword" style="font-weight: bold;">nil</span>` <span class="hljs-keyword" style="font-weight: bold;">if</span> no <span class="hljs-keyword" style="font-weight: bold;">next</span>
/<span class="hljs-regexp" style="color: rgb(0, 153, 38);">//</span> element exists.
public mutating func <span class="hljs-keyword" style="font-weight: bold;">next</span>() -> <span class="hljs-constant" style="color: rgb(0, 153, 153);">Element</span>? {
<span class="hljs-keyword" style="font-weight: bold;">if</span> done {
<span class="hljs-keyword" style="font-weight: bold;">return</span> <span class="hljs-keyword" style="font-weight: bold;">nil</span>
}
<span class="hljs-keyword" style="font-weight: bold;">if</span> stride > <span class="hljs-number" style="color: rgb(0, 153, 153);">0</span> ? current >= <span class="hljs-keyword" style="font-weight: bold;">end</span> <span class="hljs-symbol" style="color: rgb(153, 0, 115);">:</span> current <= <span class="hljs-keyword" style="font-weight: bold;">end</span> {
<span class="hljs-keyword" style="font-weight: bold;">if</span> current == <span class="hljs-keyword" style="font-weight: bold;">end</span> {
done = <span class="hljs-keyword" style="font-weight: bold;">true</span>
<span class="hljs-keyword" style="font-weight: bold;">return</span> current
}
<span class="hljs-keyword" style="font-weight: bold;">return</span> <span class="hljs-keyword" style="font-weight: bold;">nil</span>
}
let result = current
current += stride
<span class="hljs-keyword" style="font-weight: bold;">return</span> result
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">Proposed:</p><pre style="color: rgb(17, 17, 17); font-size: 16px;" class=""><code class="swift ruby hljs" style="display: block; padding: 0.5em; color: rgb(51, 51, 51); background-color: rgb(248, 248, 248); background-position: initial initial; background-repeat: initial initial;"> /<span class="hljs-regexp" style="color: rgb(0, 153, 38);">//</span> <span class="hljs-constant" style="color: rgb(0, 153, 153);">Advance</span> to the <span class="hljs-keyword" style="font-weight: bold;">next</span> element <span class="hljs-keyword" style="font-weight: bold;">and</span> <span class="hljs-keyword" style="font-weight: bold;">return</span> it, <span class="hljs-keyword" style="font-weight: bold;">or</span> `<span class="hljs-keyword" style="font-weight: bold;">nil</span>` <span class="hljs-keyword" style="font-weight: bold;">if</span> no <span class="hljs-keyword" style="font-weight: bold;">next</span>
/<span class="hljs-regexp" style="color: rgb(0, 153, 38);">//</span> element exists.
public mutating func <span class="hljs-keyword" style="font-weight: bold;">next</span>() -> <span class="hljs-constant" style="color: rgb(0, 153, 153);">Element</span>? {
<span class="hljs-keyword" style="font-weight: bold;">if</span> done {
<span class="hljs-keyword" style="font-weight: bold;">return</span> <span class="hljs-keyword" style="font-weight: bold;">nil</span>
}
<span class="hljs-keyword" style="font-weight: bold;">if</span> stride > <span class="hljs-number" style="color: rgb(0, 153, 153);">0</span> ? current >= <span class="hljs-keyword" style="font-weight: bold;">end</span> <span class="hljs-symbol" style="color: rgb(153, 0, 115);">:</span> current <= <span class="hljs-keyword" style="font-weight: bold;">end</span> {
<span class="hljs-regexp" style="color: rgb(0, 153, 38);">//</span> <span class="hljs-constant" style="color: rgb(0, 153, 153);">NOTE</span><span class="hljs-symbol" style="color: rgb(153, 0, 115);">:</span> `current >= <span class="hljs-keyword" style="font-weight: bold;">end</span>` <span class="hljs-keyword" style="font-weight: bold;">and</span> <span class="hljs-keyword" style="font-weight: bold;">not</span> `current == <span class="hljs-keyword" style="font-weight: bold;">end</span>`
<span class="hljs-keyword" style="font-weight: bold;">if</span> current >= <span class="hljs-keyword" style="font-weight: bold;">end</span> {
done = <span class="hljs-keyword" style="font-weight: bold;">true</span>
<span class="hljs-keyword" style="font-weight: bold;">return</span> current
}
<span class="hljs-keyword" style="font-weight: bold;">return</span> <span class="hljs-keyword" style="font-weight: bold;">nil</span>
}
let result = current
current += stride
<span class="hljs-keyword" style="font-weight: bold;">return</span> result
}
}
</code></pre><h2 id="introducedchanges" style="color: rgb(17, 17, 17); margin: 0px 0px 0.9545454545454546em; font-size: 1.375em; line-height: 0.9545454545454546em; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Introduced Changes</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">Under these changes, the following progression ends at 2.0 not 1.9:</p><pre style="color: rgb(17, 17, 17); font-size: 16px;" class=""><code class=" lasso hljs" style="display: block; padding: 0.5em; color: rgb(51, 51, 51); background-color: rgb(248, 248, 248); background-position: initial initial; background-repeat: initial initial;">print(<span class="hljs-built_in" style="color: rgb(0, 134, 179);">Array</span>(<span class="hljs-number" style="color: rgb(0, 153, 153);">1.0</span><span class="hljs-built_in" style="color: rgb(0, 134, 179);">.</span>stride(through: <span class="hljs-number" style="color: rgb(0, 153, 153);">2.0</span>, <span class="hljs-keyword" style="font-weight: bold;">by</span>: <span class="hljs-number" style="color: rgb(0, 153, 153);">0.1</span>)))
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]</span></code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">Integer progressions are unchanged:</p><pre style="color: rgb(17, 17, 17); font-size: 16px;" class=""><code class=" lasso hljs" style="display: block; padding: 0.5em; color: rgb(51, 51, 51); background-color: rgb(248, 248, 248); background-position: initial initial; background-repeat: initial initial;">print(<span class="hljs-built_in" style="color: rgb(0, 134, 179);">Array</span>(<span class="hljs-number" style="color: rgb(0, 153, 153);">1.</span>stride(through2: <span class="hljs-number" style="color: rgb(0, 153, 153);">10</span>, <span class="hljs-keyword" style="font-weight: bold;">by</span>: <span class="hljs-number" style="color: rgb(0, 153, 153);">1</span>)))
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">/// prints [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]</span></code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">Floating point strides will extend up-to or past the <code class="">through</code> value:</p><pre style="color: rgb(17, 17, 17); font-size: 16px;" class=""><code class=" lasso hljs" style="display: block; padding: 0.5em; color: rgb(51, 51, 51); background-color: rgb(248, 248, 248); background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// Old</span>
print(<span class="hljs-built_in" style="color: rgb(0, 134, 179);">Array</span>(<span class="hljs-number" style="color: rgb(0, 153, 153);">1.0</span><span class="hljs-built_in" style="color: rgb(0, 134, 179);">.</span>stride(through: <span class="hljs-number" style="color: rgb(0, 153, 153);">1.9</span>, <span class="hljs-keyword" style="font-weight: bold;">by</span>: <span class="hljs-number" style="color: rgb(0, 153, 153);">0.25</span>)))
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// prints [1.0, 1.25, 1.5, 1.75]</span>
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// New</span>
print(<span class="hljs-built_in" style="color: rgb(0, 134, 179);">Array</span>(<span class="hljs-number" style="color: rgb(0, 153, 153);">1.0</span><span class="hljs-built_in" style="color: rgb(0, 134, 179);">.</span>stride(through: <span class="hljs-number" style="color: rgb(0, 153, 153);">1.9</span>, <span class="hljs-keyword" style="font-weight: bold;">by</span>: <span class="hljs-number" style="color: rgb(0, 153, 153);">0.25</span>)))
<span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// prints [1.0, 1.25, 1.5, 1.75, 2.0]</span></code></pre><h2 id="alternatesconsidered" style="color: rgb(17, 17, 17); margin: 0px 0px 0.9545454545454546em; font-size: 1.375em; line-height: 0.9545454545454546em; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Alternates Considered</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; font-size: 1.1429em; line-height: 1.3125em; margin: 1.3125em 0px;" class="">Other changes could include: </p><ul style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; font-size: 16px;" class=""><li style="font-size: 18px;" class="">Introducing a <code class="">digitalStride</code> function with a set precision that works in integer math, multiplying each value by 10<sup style="font-size: 1.4ex; height: 0px; line-height: 1; position: relative;" class="">n</sup>, converting to integers, and then working back to floating point after each change</li><li style="font-size: 18px;" class="">Counting expected iterations by forming <code class="">(max - min) / by</code>, e.g. <code class="">(2.0 - 1.0) / 0.1</code>, which is 10, and performing each step as a pro-rated progression along those steps, which would remove most of the accumulated floating point errors along the way.</li><li style="font-size: 18px;" class="">Introducing a <code class="">DecimalNumber</code> type, with its own <code class="">stride</code> methods, e.g. <code class="">DecimalNumber(1.0).stride(through:DecimalNumber(2.0), by: DecimalNumber(0.1))</code>.</li></ul><div class=""><br class=""></div><div class=""><br class=""></div><div><blockquote type="cite" class=""><div class="">On Feb 26, 2016, at 5:12 PM, Erica Sadun via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=us-ascii" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><font class="">I have a problem with the way floating point ranges work with striding:</font></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><font class=""><font face="Courier" class="">1.0.stride(through: 2.0, by: 0.1)</font></font><span style="font-family: Palatino-Roman;" class=""> returns the sequence [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]. </span></blockquote><blockquote class="" style="font-family: Palatino-Roman; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class=""><i class="">Documentation: "It returns the sequence where last is less than or equal to `end`." </i></div></blockquote><font face="Palatino-Roman" class=""><div class="">(And yes, the same issue exists with tradition C-style for loops).</div></font><div class="" style="font-family: Palatino-Roman;"><div class=""><br class=""></div><div class="">Would it be really horrible if the implementation and definition was changed to: </div></div><blockquote class="" style="font-family: Palatino-Roman; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class=""><div class=""><i class="">"It returns the sequence where last is greater than or equal to `end`?"</i></div></div></blockquote><div class="" style="font-family: Palatino-Roman;"><div class="">This would offer no change for integers, and include 2.0 for floating point sequences. </div><div class=""><br class=""></div><div class="">Alternatively, could there be <font face="Courier" class="">decimalStride</font>? Using Double but a rounding system with a fixed number of decimal places (e.g. 1, 2, 3), to ensure at least the end point is hit? It might look like:</div><div class=""><blockquote style="font-family: Palatino; margin: 0px 0px 0px 40px; border: none; padding: 0px;" class=""><font class=""><font face="Courier" class="">1.0.stride(through: 2.0, by: 0.1, places: 1)</font></font></blockquote><blockquote class="" style="margin: 0px 0px 0px 40px; border: none; padding: 0px;"></blockquote></div><div class=""><br class=""></div><div class="">I know there have been several discussions on-list about decimal number systems (<a href="http://article.gmane.org/gmane.comp.lang.swift.evolution/7130/match=decimal" class="">Re: Is there a need for a Decimal type?</a>) as well, but this could fix an ongoing annoyance without a major change.</div><div class=""><br class=""></div><div class="">Thanks for your thoughts,</div><div class=""><br class=""></div><div class="">-- Erica</div><div class=""><br class=""></div></div></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></div></body></html>