Strider with 'from', 'to', 'by 'and 'tolerance' parameters .


(Ted van Gaalen) #1

Hello.
P R E L I M I N A R Y
I have made a strider as described here:
it has been built in playground.

// Implementation of a strider function
// with *from* *to* *by* and *tolerance* values

/// StriderGenerator
/// constructor parms:
/// from: start value.
/// to: end value.
/// by: stepping value.
/// tolerance: this should be a value that makes sense in relation to the range magnitude.

/// Functional description:
/// Strides through a range, bounded by 'from' and 'to', moving with the amount in 'by'.
/// A tolerance value is applied with bounds-testing because floating point numbers have a finite
/// precision, due to storage limitations of today's computers.
/// Note that it is also possibe to move backwards e.g.
/// for val in 10.0.strider(to: -10 , by: 0.5, tolerance: 0.0001)
///
/// made by Ted van Gaalen, based upon previously existing examples of strider variants
/// as found in swift-evolution.org
/// 3.3.2016
///
/// This code runs OK in Xcode Playground, however:
/// WARNING! This code has yet to be tested more thoroughly before applying/installing it!
///

public struct StriderGenerator : GeneratorType
{
    private let low: Double
    private let high: Double
    private var step : Double
    private var tol : Double

    private var iterator = 0

    private let moveForward: Bool
    
    private var done = false
        
    public init(from: Double, to: Double, by: Double, tolerance: Double)
    {
        step = by
        if from < to
        {
            low = from
            high = to
            moveForward = true
        }
        else
        {
            low = to
            high = from
            moveForward = false
        }
        self.tol = tolerance * 0.5 // center it.
    }
    
    /// return next value or nil, if no next
    /// element exists.
    
    public mutating func next() -> Double?
    {
        let current:Double
        if done
        {
            return nil
        }
        
        if moveForward
        {
            current = low + Double(iterator) * step
        }
        else
        {
            current = high - Double(iterator) * step
        }
        iterator += 1
        
        // done if exceeding low or highlimits + tolerance
        
        done = current > high + tol ||
               current < low - tol
        
        if done
        {
            return nil
        }
        else
        {
            return current
        }
    }
}

public struct Strider : SequenceType // Aragorn
{
    private let start: Double
    private let end: Double
    private let step: Double
    private let tol: Double

    init(from: Double, to: Double, by: Double, tolerance : Double)
    {
        _precondition(by > 0.0 ,
            "Init of struct Strider: 'by:...' value must be > 0.0.")
        _precondition(abs(by) > tolerance,
            "Init of struct Strider: 'by:...' value must be > tolerance.")
        _precondition(tolerance >= 0.0,
            "Init of struct Strider: tolerance:... value must be >= 0.0")
        
        start = from
        end = to;
        step = by
        tol = tolerance
    }
    
    /// Return a *generator* over the elements of this *sequence*.
    
    public func generate() -> StriderGenerator
    {
        return StriderGenerator(from: start, to: end, by: step, tolerance: tol)
    }
}

public extension Double
{
    
    public func strider(to to: Double, by: Double, tolerance: Double ) -> Strider
    {
        return Strider( from: self, to: to, by: by, tolerance: tolerance)
    }
}

print("Testing the new .strider extension")

let testvalues =
[
    // fr: to: by: tolerance:
    [ 0.0, 5.0, 1.0, 0.0 ],
    [-3.0, 4.0, 0.12, 0.1 ],
    [ 2.0, -1.0, 0.34, 0.1 ],
    [ 0.001, -0.002, 0.0001, 0.00001 ]
]

for parm in testvalues
{
    
    print("==============Stride from: \(parm[0]) to: \(parm[1]) by: \(parm[2]) tolerance: \(parm[3])\n")
    
    for val in parm[0].strider(to: parm[1], by: parm[2], tolerance: parm[3])
    {
        print("\(val) ", terminator:"")
    }
    print("\n\n")
}

Hereunder are a few test results from the above code running in Xcode Playground,
More testing is needed (anyone ?),
just fill up the 2d array ‘testvalues'
Of course suggestions for improvements are welcome.

Kind Regards from a Dutch programmer who lives in southern Germany

TedvG

==============Stride from: 0.0 to: 5.0 by: 1.0 tolerance: 0.0

0.0 1.0 2.0 3.0 4.0 5.0

==============Stride from: -3.0 to: 4.0 by: 0.12 tolerance: 0.1

-3.0 -2.88 -2.76 -2.64 -2.52 -2.4 -2.28 -2.16 -2.04 -1.92 -1.8 -1.68 -1.56 -1.44 -1.32 -1.2 -1.08 -0.96 -0.84 -0.72 -0.6 -0.48 -0.36 -0.24 -0.12 0.0 0.12 0.24 0.36 0.48 0.6 0.72 0.84 0.96 1.08 1.2 1.32 1.44 1.56 1.68 1.8 1.92 2.04 2.16 2.28 2.4 2.52 2.64 2.76 2.88 3.0 3.12 3.24 3.36 3.48 3.6 3.72 3.84 3.96

==============Stride from: 2.0 to: -1.0 by: 0.34 tolerance: 0.1

2.0 1.66 1.32 0.98 0.64 0.3 -0.04 -0.38 -0.72

==============Stride from: 0.001 to: -0.002 by: 0.0001 tolerance: 1e-05

0.001 0.0009 0.0008 0.0007 0.0006 0.0005 0.0004 0.0003 0.0002 9.99999999999999e-05 0.0 -0.0001 -0.0002 -0.0003 -0.0004 -0.0005 -0.0006 -0.0007 -0.0008 -0.0009 -0.001 -0.0011 -0.0012 -0.0013 -0.0014 -0.0015 -0.0016 -0.0017 -0.0018 -0.0019 -0.002