Units and Measurements in Swift - swift

I am trying to make a measurement entry keyboard class that has a text field for the value, and a picker for the unit.
I am having trouble using the Measurement type because of its generic behaviour.
I would like my class to return a new Measurement instance based on the initial value, but allowing the user to change the unit without necessarily converting it. I get the error "Cannot convert value of type 'Unit' to expected argument type 'T'" on the line where I am initialising meas with a value, and a unit of a different type.
Also, is there an way to iterate through the built-in sub units of a Dimension Type? i.e get all the sub units of UnitPressure for example.
If someone could just point me to an answer about similar generic behaviour, that would be much appreciated.
class MeasurementPicker<T : Dimension> {
init(initialUnit: Measurement<T>) {
self.initUnit = initialUnit
}
var initUnit: Measurement<T>
func getUnitList() -> [Unit]? {
switch initUnit.unit.self {
case is UnitPressure:
let retUnits: [Unit]? = [
UnitPressure.bars,
UnitPressure.gigapascals,
UnitPressure.hectopascals,
UnitPressure.inchesOfMercury,
UnitPressure.kilopascals,
UnitPressure.megapascals,
UnitPressure.millibars,
UnitPressure.millimetersOfMercury,
UnitPressure.newtonsPerMetersSquared,
UnitPressure.poundsForcePerSquareInch
]
return retUnits
default:
return nil
}
}
func getNewType(index: Int) -> Measurement<T> {
let myNewUnit : Unit = getUnitList()![index]
var meas = Measurement<T>.init(value: 6, unit: myNewUnit)
}
}
let x = Measurement(value: 5.5, unit: UnitPressure.kilopascals)
let y = MeasurementPicker<UnitPressure>(initialUnit: x)
let z = y.getNewType(index: 0)
print(z.unit.symbol)

There is no need to create a generic class. Just create a unit or dimension property. And in your method getNewType return Measurement<Unit> or just the unit or the dimension. You can also just use a subscript to get the dimension from your list:
class MeasurementPicker {
init(dimension: Dimension) { self.dimension = dimension }
var dimension: Dimension
var list: [Dimension] {
switch dimension.self {
case is UnitPressure:
return [UnitPressure.bars,
UnitPressure.gigapascals,
UnitPressure.hectopascals,
UnitPressure.inchesOfMercury,
UnitPressure.kilopascals,
UnitPressure.megapascals,
UnitPressure.millibars,
UnitPressure.millimetersOfMercury,
UnitPressure.newtonsPerMetersSquared,
UnitPressure.poundsForcePerSquareInch]
default: return []
}
}
}
Usage:
let y = MeasurementPicker(dimension: UnitPressure.kilopascals)
let z = y.list[0]
print(z.symbol) // "bar\n"

Related

What does this function actually do?

i am currently trying to do some self learning in swift just for my own interest. in the course i bought it says that we should create a function similar to this one in order to solve my problem. but I'm blankly staring trying to figure out what this function actually does?
func unknown() -> () -> Int {
var x = 0
let z: () -> Int = {
x += 1
return x
}
return z
}
It is a function that returns another function which will return an integer that will be increased everytime you call it:
let afunc = unknown()
let value1 = afunc() // 1
let value2 = afunc() // 2
let value3 = afunc() // 3
The interesting part of this is the return type. () -> Int is a function that returns an Int, which means that unknown returns a function rather than something simple, like a number.
z is then a variable of that same type and is assigned a function definition to be returned.
If you assign the result of unknown to a variable, you can then invoke the returned function.
This implementation of a high order function is an interesting way of defining generators. An infinite sequence-like class would've achieve the same thing, but with more verbosity:
class MySequence {
private var x = 0
func unknown() -> Int {
x += 1
return x
}
}
var seq = MySequence()
let unknown = seq.unknown
print(unknown()) // 1
print(unknown()) // 2
print(unknown()) // 3
// ... and so on
The main difference between the class and the anonymous closure is the storage for x: the closure captures in due to using the variables within its body, while the class declares explicit storage for the property.
Some fancy stuff can result by using high order functions, like a generator for the Fibonacci numbers:
func fibonnaciSequence() -> () -> Int? {
var a = 0, b = 1
return { let c = a; a += b; b = c; return c }
}
let fibo = fibonnaciSequence()
while let f = fibo() {
// this will print forever
// actually not forever, it will stop at some point due to += overflowing
print(f)
}

How to store a generic Measurement in Swift?

I'd like to have a variable which can represent a single Measurement but may have different unit types. For example, it could store a length or a mass. It seems so simple, but I can't figure it out.
Here's what I tried:
struct Data {
var weight: Measurement<UnitMass>
var length: Measurement<UnitLength>
var target = "Weight"
var valueOfTarget: Measurement<Unit> {
if target == "Weight" {
return weight
} else {
return length
}
}
}
var data = Data(weight: Measurement<UnitMass>(value: 10, unit: UnitMass.kilograms),
length: Measurement<UnitLength>(value: 10, unit: UnitLength.centimeters),
target: "Weight")
print(data.valueOfTarget)
I also tried using <Dimension> as suggested in another answer, but that had a similar error.
This results in a compiler error:
error: cannot convert return expression of type 'Measurement<UnitMass>' to return type 'Measurement<Unit>'
Am I'm missing something obvious or is this just not possible?
You could just create new generic return values. This seems to compile OK for me.
struct Data {
var weight: Measurement<UnitMass>
var length: Measurement<UnitLength>
var target = "Weight"
var valueOfTarget: Measurement<Unit> {
if target == "Weight" {
return Measurement<Unit>(value: weight.value, unit: weight.unit)
} else {
return Measurement<Unit>(value: length.value, unit: length.unit)
}
}
}
var data = Data(weight: Measurement<UnitMass>(value: 10, unit: UnitMass.kilograms),
length: Measurement<UnitLength>(value: 10, unit: UnitLength.centimeters),
target: "Weight")
print(data.valueOfTarget)
First, don't make your own struct named Data, because Foundation already has a type named Data that's pretty widely used.
Second, since (in a comment) you said “It's for weight loss, the user can choose a target as either a weight or a waist size”, it seems like you should probably model this using an enum rather than a struct:
enum WeightLossTarget {
case weight(Measurement<UnitMass>)
case waistSize(Measurement<UnitLength>)
}
Third, if you really need to use a struct, you can fall back to the Objective-C type NSMeasurement for non-generic type:
struct WeightLossTarget {
enum TargetType {
case weight
case waistSize
}
var weight: Measurement<UnitMass>
var waistSize: Measurement<UnitLength>
var target: TargetType
var valueOfTarget: NSMeasurement {
switch target {
case .weight: return weight as NSMeasurement
case .waistSize: return waistSize as NSMeasurement
}
}
}

Mark closure element mutable Swift

I have 2 structs, first is:
struct LineData {
init (name: String,
colorValue: String,
values: [Int]){
self.name = name
self.colorValue = colorValue
self.values = values
}
private var cachedMaxValue: Int? = nil
let name: String
let colorValue: String
let values: [Int]
// describe max value for Y axis for specific Line
mutating func maxValue() -> Int{
if let cached = cachedMaxValue {
return cached
}
self.cachedMaxValue = values.max()
return cachedMaxValue ?? 0
}
}
Second have array of LineData structs:
struct CharData {
init(xAxis: XAxis,
lines: [LineData]){
self.xAxis = xAxis
self.lines = lines
}
private var cachedMaxValue: Int? = nil
var xAxis: XAxis
var lines: [LineData]
// describe max value for Y axis among lines
func maxValue() -> Int{
var maxValues: [Int] = []
lines.forEach{it in
maxValues.append(it.maxValue())
}
return 0
}
}
Code above not compile, because, of error on method maxValues for struct CharData. It says Cannot use mutating member on immutable value: 'it' is a 'let' constant
What i want is, iterate through an array of lines and among it max values find greater value.
Since lines is an ordinary array, how about simply:
for i in 0..<lines.count {
maxValues.append(lines[i].maxValue())
}
perhaps not quite as Swifty, but nothing gets copied. The optimizer ought to give you pretty much the same performance as forEach.
It's the it parameter/object in the forEach that's immutable. Just like the error says: "it is a let". You could probably do something like this:
lines.forEach { it in
var mutableIt = it
maxValues.append(mutableIt.maxValue())
}
It should be noted that this will create a mutable copy of the "it" struct instance.

Functional programming way of doing array conversion

struct MapVector {
var distance: Double
var bearing: Double
}
func distanceAndBearing() -> [MapVector] {
var points = self.mapPoints
var currPoint:CLLocation = points.first!
points.removeAtIndex(0)
var result: [MapVector] = []
for point: CLLocation in points {
let calc = PointCalculator(initialPoint: currPoint, nextPoint: point)
let v = MapVector(distance: calc.pointDistance, bearing: calc.bearing)
result.append(v)
currPoint = point
}
return result
}
I am working in Swift on an application using map coordinates. I have a an array of CLLocations from which I would like to create an array of distances and bearings. The above code (its slightly simplified for readability, so may not be 100% correct) achieves that but I'd like to do it in a neater way. Is this something that can be done with map or filter? Still trying to get my head around the FP way of doing things.
Here is a simplified example for the same problem except the calculations:
let numbers = [3, 7, 2, 8, 3, 7, 5]
let result = numbers.isEmpty ? [] :
map(zip(numbers, numbers[1..<numbers.count])) {
(x, y) in
return (diff: x - y, mult: x * y)
}
result[0].diff // -4
result[0].mult // 21
Here I compute the differences and the multiplications of the numbers.
Note this will work only for Swift 1.2
In case you need it for earlier version, you should explore the use of Zip2.
For reference here are alternative solutions I came up with:-
func distanceAndBearing2() -> [MapVector]
{
// make the removeAtIndex(0) part safe
if (self.mapPoints.count == 0) {
return []
}
var t2 = self.mapPoints
t2.removeAtIndex(0)
let t3 = zip(self.mapPoints, t2)
return Array(t3).map({
(p1, p2) in
return PointCalculator(initialPoint: p1, nextPoint: p2).toMapVector()
})
}
This uses the new zip method from Xcode 6.3 Beta 2, and I moved the conversion to MapVector into the PointCalculator struct
func distanceAndBearing3() -> [MapVector] {
// make the shift part safe
if (self.mapPoints.count == 0) {
return []
}
var points = self.mapPoints
var currPoint = points.shift()!
return points.map {
point in
let initialPoint = currPoint
currPoint = point
return LocationPair(initialPoint: initialPoint,
nextPoint: point).toMapVector()
}
}
And this version uses the same toMapVector method on the PointCalculator struct, but uses a variable outside the map function which is updated by the map function; this feels like its not "correct"

How would I create a constant that could be one of several strings depending on conditions?

I want to have a constant using let that may be one of several values.
For instance:
if condition1 {
constant = "hi"
}
else if condition2 {
constant = "hello"
}
else if condition3 {
constant = "hey"
}
else if condition4 {
constant = "greetings"
}
I'm not sure how to do this with Swift and the let feature. But I'm inclined to believe it's possible, as this is in the Swift book:
Use let to make a constant and var to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once.
How would I accomplish this?
As pointed out in the other answers you can't directly do this. But if you're looking to just variably set the initial value of a constant, then yes, that is possible. Here's an example with a computed property.
class MyClass {
let aConstant: String = {
if something == true {
return "something"
} else {
return "something else"
}
}()
}
I think you are looking for variable which will be assigned later inside switch-case:
let constant :String
switch conditions {
case condition1:
constant = "hi"
case condition2:
constant = "hello"
case condition3:
constant = "hey"
case condition4:
constant = "greetings"
default:
constant = "salute"
}
One option would be something like this, using a closure:
let constant: String = ({ value in
if conditionOne {
return "Hi"
} else if conditionTwo {
return "Bye"
}
return "Oops!"
})(myData /*needed for condition*/)
Or, for another twist, using generics:
func fancySwitch<S, T>(val: S, fn: S -> T) -> T {
return fn(val)
}
let x: String = fancySwitch(3) { val in
if val == 2 {
return "Hi"
} else if val < 5 {
return "Bye"
}
return "Oops"
}
let y: String = fancySwitch((3, 4)) { (a, b) in
if a == 2 {
return "Hi"
} else if b < 5 {
return "Bye"
}
return "Oops"
}
I understand what you're looking for. In Scala and some other functional languages this can be done using the match statement (kind of like switch) because the entire statement resolves to a value like this:
val b = true
val num = b match {
case true => 1
case false => 0
}
This is unfortunately not directly possible in Swift because there is no way to get a value from a branch statement. As stated in the Swift book, "Swift has two branch statements: an if statement and a switch statement." Neither of these statements resolve to a value.
The closest code structure I can think of is to first use a variable to retrieve the correct value and then assign it to a constant to be used in any later code:
let b = true
var num_mutable: Int
switch b {
case true:
num_mutable = 1
default:
num_mutable = 0
}
let num = num_mutable
Just add the line let constant: String before your if/else statement.
Below, an excerpt from Swift 1.2 and Xcode 6.3 beta - Swift Blog - Apple Developer elaborates.
let constants are now more powerful and consistent — The new rule is
that a let constant must be initialized before use (like a var), and
that it may only be initialized, not reassigned or mutated after
initialization. This enables patterns like:
let x : SomeThing
if condition {
x = foo()
} else {
x = bar()
}
use(x)
This formerly required the use of a var even though there is no
mutation taking place. Properties have been folded into this model to
simplify their semantics in initializers as well.
I found the Swift blog post above from the article "Let It Go: Late Initialization of Let in Swift", which I found by googling: swift let constant conditional initialize.