Proper application of encapsulation - swift

I've been researching the concept of encapsulation and found some decent threads about the topic like this one and this one. But I haven't been able to find an answer to a particular question I have. I'll use an example in Swift.
Say you have an object that is of type RoadTrip:
class RoadTrip() {
private var duration: Double
private var totalMiles: Double
private var gallonsOfFuel: Double
var averageMilesPerGallon: Double
}
Now let's say the app is going to calculate the averageMilesPerGallon which is the only public property:
func calculateAverageMilePerGallon() -> Double {
let mpg = totalMiles / gallonsOfFuel
return mpg
}
Should the calculation of average miles per gallon be a private method of the RoadTrip object that executes and updates its averageMilesPerGallon or would it be acceptable to have the calculation performed by another method in a separate utility class that then updates the averageMilesPerGallon property of the RoadTrip object using a mutator method that will set the value?
EDIT: Here's my single class that contains my app's basic calculations. I approached it this way based on what I learned in the Stanford course on iTunes, but I'm beginning to think in my case I should move much of this to my LiftEvent class:
infix operator ^^ { }
func ^^ (radix: Double, power: Double) -> Double {
return Double(pow(Double(radix), Double(power)))
}
class CalculatorBrain: NSObject {
var weightLifted: Double?
var repetitions: Double?
var oneRepMax: Double?
let dataManager = LiftEventDataManager()
func calculateOneRepMax(weightLifted: Double, repetitions: Int ) -> Double {
let preferredFormulaID = UserDefaultsManager.sharedInstance.preferredFormula!
let formulas = dataManager.fetchSelectableFormulas()
let formulaName = formulas[preferredFormulaID].formulaName
switch formulaName {
case "Epley":
oneRepMax = weightLifted * (1 + Double(repetitions)/30.0)
return oneRepMax!
case "Baechle":
oneRepMax = weightLifted * (36/(37 - Double(repetitions)))
return oneRepMax!
case "Brzychi":
oneRepMax = weightLifted * ( 1 + ( 0.033 * Double(repetitions)))
return oneRepMax!
case "Lander":
oneRepMax = 100 * weightLifted / (101.3 - (2.67123 * Double(repetitions)))
return oneRepMax!
case "Lombardi":
oneRepMax = weightLifted * (Double(repetitions) ^^ 0.10)
return oneRepMax!
case "Mayhew":
oneRepMax = 100 * weightLifted / (52.2 + (41.9 * (2.71828 ^^ (-0.055 * Double(repetitions)))))
return oneRepMax!
case "O'Conner":
oneRepMax = weightLifted * (1 + 0.025 * Double(repetitions))
return oneRepMax!
default:
return 0.0
}
}
private func calculatePercentOfWeight(maxWeight: Double, percent: Double) -> Double {
return maxWeight * percent
}
func calculateWeightPercentages(maxWeight: String) -> [Int: Double] {
let weightPercentages = [1.0, 0.95, 0.90, 0.85, 0.80, 0.75, 0.70, 0.65, 0.60, 0.55, 0.50, 0.45, 0.40, 0.35, 0.30, 0.25]
var percentages = [Int: Double]()
for percent in weightPercentages {
let integerPercent = Int(percent * 100)
percentages[integerPercent] = calculatePercentOfWeight(Double(maxWeight)!, percent: percent)
}
return percentages
}
func convertBetweenUnits(fromUnit: Int, toUnit: Int, value: Double) -> Double {
let units = dataManager.fetchUnits()
let from = units[fromUnit].conversionRatio as Double
let to = units[toUnit].conversionRatio as Double
let result = Double(value * to / from)
return result
}
}

I think this is the ideal use-case for a computed property:
class RoadTrip {
private let duration: Double
private let totalMiles: Double
private let gallonsOfFuel: Double
private var averageMilesPerGallon: Double {
return totalMiles / gallonsOfFuel
}
}

Related

How can you create a generic struct for Numeric numbers that can calculate a percentage?

This doesn't compile because Initializer 'init(_:)' requires that 'Number' conform to 'BinaryInteger'
struct Percentage<Number: Numeric> {
let value: Number
let total: Number
var percentage: Double {
Double(value) / Double(total)
}
}
Does anyone have a nice solution?
To give some context to the problem from real life: I'm coding a SwiftUI app, that has a CircularProgress-view. I would like to use the same CircularProgress-view with different number types and to be able to show the current value in proportion to min and max. To do that, I need to solve the problem above.
The main issue is that Numeric doesn't support generic divisions. One possible solution is to provide multiple generic methods to support different protocols (Integer/FloatingPoint) and Decimal as well:
extension Decimal {
var number: NSDecimalNumber { self as NSDecimalNumber }
var double: Double { number.doubleValue }
}
struct Percentage<T: Numeric> {
let value: T
let total: T
func percentage<F: BinaryFloatingPoint>() -> F where T: BinaryFloatingPoint {
F(value) / F(total)
}
func percentage<F: BinaryFloatingPoint>() -> F where T: BinaryInteger {
F(value) / F(total)
}
func percentage<F: BinaryFloatingPoint>() -> F where T == Decimal {
F(value.double) / F(total.double)
}
func percentage() -> Decimal where T == Decimal {
value / total
}
}
let percentageInt = Percentage<Int>(value: 10, total: 100)
let result1: Double = percentageInt.percentage() // 0.1
let percentageDouble = Percentage<Double>(value: 10, total: 100)
let result2: Double = percentageDouble.percentage() // 0.1
let result3: CGFloat = percentageDouble.percentage() // 0.1
let percentageDecimal = Percentage<Decimal>(value: 10, total: 100)
let result4 = percentageDecimal.percentage() // 0.1 decimal
let result5: Double = percentageDecimal.percentage() // 0.1
You can create extensions on Percentage where you restrict Number to BinaryInteger and FloatingPoint to be able to use the / operator.
struct Percentage<Number: Numeric> {
let value: Number
let total: Number
}
extension Percentage where Number: BinaryInteger {
var percentage: Double {
Double(value) / Double(total)
}
}
extension Percentage where Number: FloatingPoint {
var percentage: Number {
value / total
}
}
Percentage(value: 15, total: 60).percentage // 25.0
Percentage(value: 1.5, total: 3.0).percentage // 50.0

Swift specify double without converting to string

I want to specify my (slider) double to 2 decimals but xcode won't let me do that:
return (Double(pris, specifier: "%.2f"))
And i don't want to convert it into a string and then format it because numbers like 600000000 are then unreadable.
I have tried solutions like :
extension Double {
// Rounds the double to 'places' significant digits
func roundTo(places:Int) -> Double {
guard self != 0.0 else {
return 0
}
let divisor = pow(10.0, Double(places) - ceil(log10(fabs(self))))
return (self * divisor).rounded() / divisor
}
}
let b: Double = Double(600000000.376)
let result = Double(round(100*b)/100)
print(result) // prints 600000000.38
This should do what you need:
extension Double {
func roundedTo(places: Int) -> Double {
let conversion = pow(10.0, Double(places))
return (self * conversion).rounded() / conversion
}
}
print(10.125.roundedTo(places: 2)) // prints 10.13
print(10.124.roundedTo(places: 2)) // prints 10.12
The simple solution was to remove the (Double) before the calculation.
return (Double(pris, specifier: "%.2f"))
should be only
pris, specifier: "%.2f")

How can I return a float or double from normal distribution in Swift 4?

I am trying to return a float or a double from a normal distribution with mean = 0 and standard deviation = 4 in Swift4. The closest I can come to getting what I need is by using GameplayKit -> GKGaussianDistribution as implemented in the code below:
func generateForecast() {
let gauss = GKGaussianDistribution(randomSource: self.source, mean: 0.0, deviation: 4.0)
self.epsilon = gauss.nextInt()
}
My problem is when I call
gauss.nextInt()
I obviously get an integer. And when I try
gauss.nextUniform()
I get a number between -1 and 1.
Is there a fairly simple way to return a float or double from a normal distribution in Swift4 instead of an Int or a float between -1 and 1?
import AppKit
import PlaygroundSupport
import GameplayKit
let nibFile = NSNib.Name(rawValue:"MyView")
var topLevelObjects : NSArray?
Bundle.main.loadNibNamed(nibFile, owner:nil, topLevelObjects: &topLevelObjects)
let views = (topLevelObjects as! Array<Any>).filter { $0 is NSView }
// Present the view in Playground
PlaygroundPage.current.liveView = views[0] as! NSView
let s = 0.001
var auto_corr: [Int] = []
class Market {
var numAgents: Int
var traders: [Agent] = []
var price: Double
var epsilon: Int
var priceHist: [Double] = []
var returnHist: [Double] = []
var returnRealHist: [Double] = []
var logReturn: Double = 0
var realReturn: Double = 0
let source = GKRandomSource()
init(numAgents: Int, price: Double, epsilon: Int) {
self.numAgents = numAgents
self.price = price
self.epsilon = epsilon
for _ in 1...numAgents {
self.traders.append(Agent(phi: 1, theta: 1))
}
}
func generateForecast() {
let gauss = GKGaussianDistribution(randomSource: self.source, mean: 0.0, deviation: 4.0)
self.epsilon = gauss.nextInt()
}
}
The documentation for GKGaussianDistribution does not mention that it overrides nextUniform() from the base class so don't assume it will return normally distributed values for you:
You can roll your own Gaussian Distribution using the Box-Muller Transformation:
class MyGaussianDistribution {
private let randomSource: GKRandomSource
let mean: Float
let deviation: Float
init(randomSource: GKRandomSource, mean: Float, deviation: Float) {
precondition(deviation >= 0)
self.randomSource = randomSource
self.mean = mean
self.deviation = deviation
}
func nextFloat() -> Float {
guard deviation > 0 else { return mean }
let x1 = randomSource.nextUniform() // a random number between 0 and 1
let x2 = randomSource.nextUniform() // a random number between 0 and 1
let z1 = sqrt(-2 * log(x1)) * cos(2 * Float.pi * x2) // z1 is normally distributed
// Convert z1 from the Standard Normal Distribution to our Normal Distribution
return z1 * deviation + mean
}
}
I intentionally did not subclass it from GKRandomDistribution since there are other methods I need to override but are not relevant to this question.

Swift: How to make this function generic

Here is what I have:
class func truncateTailsOfRange(range: Range<Int>, portion: Double) -> Range<Int> {
let start = range.startIndex + Int(portion * Double(range.count))
let end = range.endIndex - Int(portion * Double(range.count))
return Range(start: start, end: end)
}
I would like to make this generic for IntegerType:
class func truncateTailsOfRange<T: IntegerType>(range: Range<T>, portion: Double) -> Range<T> {
let start = range.startIndex + T(portion * Double(range.count))
let end = range.endIndex - T(portion * Double(range.count))
return Range(start: start, end: end)
}
But the error I get is:
Cannot invoke initializer for type Double with an argument list of type (T.Distance)
Is this possible to do?
First you need a CustomDoubleConvertible protocol. This mirrors CustomStringConvertible. You extend all Types which you want to be convertible to Double. Similar to description which returns a String representation of a Type.
protocol CustomDoubleConvertible {
var doubleValue : Double { get }
}
extension Int : CustomDoubleConvertible {
var doubleValue : Double { return Double(self) }
}
extension Int16 : CustomDoubleConvertible {
var doubleValue : Double { return Double(self) }
}
If you make the function an extension to Range itself you can make use of it's generic nature and it's typealiases.
extension Range where Element.Distance : CustomDoubleConvertible {
Now you can calculate the offsets of the indexes like so :
let startOffset = Int(portion * self.count.doubleValue)
let endOffset = Int(portion * self.count.doubleValue)
If you further constrain Range so that it's Element must be a BidirectionalIndexType you can use successor and predecessor.
extension Range where Element.Distance : CustomDoubleConvertible, Element : BidirectionalIndexType {
This allows you to get the full function by iterating over the offsets and calling successor and predecessor.
extension Range where Element.Distance : CustomDoubleConvertible, Element : BidirectionalIndexType {
func truncateTailsOfRange(portion: Double) -> Range<Element> {
let startOffset = Int(portion * self.count.doubleValue)
let endOffset = Int(portion * self.count.doubleValue)
var start = self.startIndex
var end = self.endIndex
for _ in 0..<startOffset { start = start.successor() }
for _ in 0..<endOffset { end = end.predecessor() }
return Range(start: start, end: end)
}
}
Some tests :
let rangeA = 1...4 // 1..<5
let rangeB = "a"..."g"
rangeA.truncateTailsOfRange(0.3) // 2..<4
rangeB.truncateTailsOfRange(0.3) // has no member ....
let w : Int16 = 3
let z : Int16 = 9
let rangeC = w...z // 3..<10
rangeC.truncateTailsOfRange(0.4) // 5..<8
This is an interesting but academic exercise.
Here's a method that is not very efficient but that will work with all range types:
func truncateTailsOfRange<T>(var range: Range<T>, portion: Double) -> Range<T>
{
let elementCount = Array(range).count
let truncationCount = Int( portion * Double(elementCount) )
let remainingCount = max(0, elementCount - 2 * truncationCount)
for _ in 0..<truncationCount
{ range.startIndex = range.startIndex.successor() }
range.endIndex = range.startIndex
for _ in 0..<remainingCount
{ range.endIndex = range.endIndex.successor() }
return range
}
and here's a much quicker one :
func truncateTailsOfRange2<T>(var range: Range<T>, portion: Double) -> Range<T>
{
if range.isEmpty {return range}
let elements = Array(range)
let truncationCount = Int( portion * Double(elements.count) )
let remainingCount = max(0, elements.count - 2 * truncationCount)
return elements[truncationCount]..<elements[truncationCount+remainingCount]
}

Round Double to closest 10

I would like to round a Double to the closest multiple of 10.
For example if the number is 8.0 then round to 10.
If the number is 2.0 round it to 0.
How can I do that?
You can use the round() function (which rounds a floating point number
to the nearest integral value) and apply a "scale factor" of 10:
func roundToTens(x : Double) -> Int {
return 10 * Int(round(x / 10.0))
}
Example usage:
print(roundToTens(4.9)) // 0
print(roundToTens(15.1)) // 20
In the second example, 15.1 is divided by ten (1.51), rounded (2.0),
converted to an integer (2) and multiplied by 10 again (20).
Swift 3:
func roundToTens(_ x : Double) -> Int {
return 10 * Int((x / 10.0).rounded())
}
Alternatively:
func roundToTens(_ x : Double) -> Int {
return 10 * lrint(x / 10.0)
}
defining the rounding function as
import Foundation
func round(_ value: Double, toNearest: Double) -> Double {
return round(value / toNearest) * toNearest
}
gives you more general and flexible way how to do it
let r0 = round(1.27, toNearest: 0.25) // 1.25
let r1 = round(325, toNearest: 10) // 330.0
let r3 = round(.pi, toNearest: 0.0001) // 3.1416
UPDATE
more generic definitions
func round<T:BinaryFloatingPoint>(_ value:T, toNearest:T) -> T {
return round(value / toNearest) * toNearest
}
func round<T:BinaryInteger>(_ value:T, toNearest:T) -> T {
return T(round(Double(value), toNearest:Double(toNearest)))
}
and usage
let A = round(-13.16, toNearest: 0.25)
let B = round(8, toNearest: 3.0)
let C = round(8, toNearest: 5)
print(A, type(of: A))
print(B, type(of: B))
print(C, type(of: C))
prints
-13.25 Double
9.0 Double
10 Int
You can also extend FloatingPoint protocol and add an option to choose the rounding rule:
extension FloatingPoint {
func rounded(to value: Self, roundingRule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> Self {
(self / value).rounded(roundingRule) * value
}
}
let value = 325.0
value.rounded(to: 10) // 330 (default rounding mode toNearestOrAwayFromZero)
value.rounded(to: 10, roundingRule: .down) // 320
In Swift 3.0 it is
10 * Int(round(Double(ratio / 10)))
Extension for rounding to any number !
extension Int{
func rounding(nearest:Float) -> Int{
return Int(nearest * round(Float(self)/nearest))
}
}
Nice extension for BinaryFloatingPoint in swift:
extension BinaryFloatingPoint{
func roundToTens() -> Int{
return 10 * Int(Darwin.round(self / 10.0))
}
func roundToHundreds() -> Int{
return 100 * Int(Darwin.round(self / 100.0))
}
}
extension Double {
var roundToTens: Double {
let divideByTen = self / 10
let multiByTen = (ceil(divideByTen) * 10)
return multiByTen
}
}
usage:
36. roundToTens