<div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000;text-align: left" dir="ltr">
Hi all!<div class="mb_sig"></div>
<div><br></div><div>This breaks subclassing for Interval. TextLineInterval is an example. Yet, it is used only in ST80, so I will change it there.</div><div><br></div><div>What about #species? Or something similar? Do we really want to break subclassing on Interval?</div><div><br></div><div>Best,</div><div>Marcel</div><blockquote class="history_container" type="cite" style="border-left-style: solid;border-width: 1px;margin-top: 20px;margin-left: 0px;padding-left: 10px;min-width: 500px">
<p style="color: #AAAAAA; margin-top: 10px;">Am 03.03.2021 02:14:24 schrieb commits@source.squeak.org <commits@source.squeak.org>:</p><div style="font-family:Arial,Helvetica,sans-serif">Nicolas Cellier uploaded a new version of Collections to project The Trunk:<br>http://source.squeak.org/trunk/Collections-nice.925.mcz<br><br>==================== Summary ====================<br><br>Name: Collections-nice.925<br>Author: nice<br>Time: 3 March 2021, 2:14:13.09562 am<br>UUID: ee92856e-98d1-3a41-a3f2-3529927e7a02<br>Ancestors: Collections-jar.924<br><br>Distinguish questionable LimitedPrecisionInterval (those with Float bounds or step) from ordinary Interval of Integer, Fraction or ScaledDecimals.<br><br>The main interest of having a specific class is to avoid crippling Interval with Float workarounds, for the rare use case.<br><br>Explain some of the pitfalls of LimitedPrecisionInterval, and encourage alternatives in class comment, which is a second advantage of having a separate class.<br><br>Abandon fuzzy inclusion logic, which is considered to introduce more discrepancies than it tries to solve<br>See http://bugs.squeak.org/view.php?id=6455.<br>and method #testSurprisingFuzzyInclusion<br><br>Fix two other failing tests for Interval of Floats.<br><br>Fix size so that (0.3 to: 1.2 by: 0.1) includes: 1.2.<br>See https://github.com/dolphinsmalltalk/Dolphin/issues/1108<br>This makes size a bit less performant than super, a 3rd reason why a specific class is neat.<br><br>Huh, that's more comments than code ;).<br><br>=============== Diff against Collections-jar.924 ===============<br><br>Item was changed:<br> ----- Method: Interval class>>from:to: (in category 'instance creation') -----<br> from: startInteger to: stopInteger <br> "Answer an instance of me, starting at startNumber, ending at <br> stopNumber, and with an interval increment of 1."<br> <br>+ ^((startInteger hasLimitedPrecision or: [stopInteger hasLimitedPrecision])<br>+ ifTrue: [LimitedPrecisionInterval]<br>+ ifFalse: [Interval]) basicNew<br>- ^self basicNew<br> setFrom: startInteger<br> to: stopInteger<br> by: 1!<br><br>Item was changed:<br> ----- Method: Interval class>>from:to:by: (in category 'instance creation') -----<br> from: startInteger to: stopInteger by: stepInteger <br> "Answer an instance of me, starting at startNumber, ending at <br> stopNumber, and with an interval increment of stepNumber."<br> <br>+ ^((startInteger hasLimitedPrecision or: [stopInteger hasLimitedPrecision or: [stepInteger hasLimitedPrecision]])<br>+ ifTrue: [LimitedPrecisionInterval]<br>+ ifFalse: [Interval]) basicNew<br>- ^self basicNew<br> setFrom: startInteger<br> to: stopInteger<br> by: stepInteger!<br><br>Item was changed:<br> ----- Method: Interval>>indexOf:startingAt: (in category 'accessing') -----<br> indexOf: anElement startingAt: startIndex<br> "startIndex is an positive integer, the collection index where the search is started."<br>- "during the computation of val , floats are only used when the receiver contains floats"<br> <br>+ | index |<br>- | index val |<br> (self rangeIncludes: anElement) ifFalse: [ ^0 ].<br>+ index := (anElement - start / step) rounded + 1.<br>- val := anElement - self first / self increment.<br>- val isFloat<br>- ifTrue: [<br>- (val - val rounded) abs * 100000000 < 1 ifFalse: [ ^0 ].<br>- index := val rounded + 1 ]<br>- ifFalse: [<br>- val isInteger ifFalse: [ ^0 ].<br>- index := val + 1 ].<br>- "finally, the value of startIndex comes into play:"<br> (index between: startIndex and: self size) ifFalse: [ ^0 ].<br>+ (self at: index) = anElement ifFalse: [ ^0 ].<br> ^index!<br><br>Item was added:<br>+ Interval subclass: #LimitedPrecisionInterval<br>+ instanceVariableNames: ''<br>+ classVariableNames: ''<br>+ poolDictionaries: ''<br>+ category: 'Collections-Sequenceable'!<br>+ <br>+ !LimitedPrecisionInterval commentStamp: 'nice 3/3/2021 01:47' prior: 0!<br>+ A LimitedPrecisionInterval is an Interval whose bounds or step haveLimitedPrecision.<br>+ Due to inexact arithmetic, special precautions must be taken in the implementation,<br>+ in order to avoid unconsistent and surprising behavior as much as possible.<br>+ <br>+ Despite those efforts, LimitedPrecisionInterval is full of pitfalls.<br>+ It is recommended to avoid using LimitedPrecisionInterval unless understanding those pitfalls.<br>+ For example, (0.2 to: 0.6 by: 0.1) last = 0.5.<br>+ This interval does not includes 0.6 because (0.1*4+0.2) is slightly greater than 0.6.<br>+ Another example is that (0.2 to: 0.6 by: 0.1) does not include 0.3 but a Float slightly greater.<br>+ <br>+ A usual workaround is to use an Integer interval, and reconstruct the Float inside the loop.<br>+ For example:<br>+ (0 to: 4) collect: [:i | 0.1*i+0.2].<br>+ or better if we want to have 0.3 and 0.6:<br>+ (2 to: 6) collect: [:i | i / 10.0].<br>+ Another workaround is to not use limited precision at all, but Fraction or ScaledDecimal when possible:<br>+ (1/10 to: 7/10 by: 1/10).<br>+ (0.1s to: 0.7s by: 0.1s).<br>+ <br>+ Yet another pitfall is that optimized to:by:do: might differ from (to:by:) do:<br>+ In the former case, repeated addition of increment is used, in the later, a multiplication is used.<br>+ Observe the differences:<br>+ Array streamContents: [:str | 0 to: 3 by: 0.3 do: [:e | str nextPut: e]].<br>+ Array streamContents: [:str | (0 to: 3 by: 0.3) do: [:e | str nextPut: e]].<br>+ <br>+ There are many more discrepancies, so use carefully, or not use it at all.!<br><br>Item was added:<br>+ ----- Method: LimitedPrecisionInterval>>copyFrom:to: (in category 'copying') -----<br>+ copyFrom: startIndex to: stopIndex<br>+ startIndex = 1 ifTrue: [^super copyFrom: startIndex to: stopIndex].<br>+ stopIndex < startIndex ifTrue: [^self copyEmpty].<br>+ ^Array new: stopIndex - startIndex + 1 streamContents: [:stream |<br>+ startIndex to: stopIndex do: [:i | stream nextPut: (self at: i)]]!<br><br>Item was added:<br>+ ----- Method: LimitedPrecisionInterval>>last (in category 'accessing') -----<br>+ last <br>+ "Refer to the comment in SequenceableCollection|last."<br>+ <br>+ ^start + (step * (self size - 1))!<br><br>Item was added:<br>+ ----- Method: LimitedPrecisionInterval>>reversed (in category 'converting') -----<br>+ reversed <br>+ "There is no guaranty that super reversed would contain same elements.<br>+ Answer an Array instead"<br>+ <br>+ ^Array new: self size streamContents: [:stream | self reverseDo: [:each | stream nextPut: each]]!<br><br>Item was added:<br>+ ----- Method: LimitedPrecisionInterval>>size (in category 'accessing') -----<br>+ size<br>+ "Answer how many elements the receiver contains."<br>+ <br>+ | candidateSize |<br>+ candidateSize := (stop - start / step max: 0) rounded.<br>+ step > 0<br>+ ifTrue: [candidateSize * step + start <= stop ifTrue: [^candidateSize + 1]]<br>+ ifFalse: [candidateSize * step + start >= stop ifTrue: [^candidateSize + 1]].<br>+ ^candidateSize!<br><br><br></div></blockquote></div>