Ray Wenderlich Course Part 11 (up to lesson 101)

Yes! Managing to get in a couple of consecutive days of entries. Now, it would be easy to be disheartened after the Collections lessons, which were all very tricky! However, the comments confirm that they were confusing - not just to me. So I am going to plough on and try to get Structures done relatively quickly so I can move on to Classes next, then will be able to start the next project! That's the intention anyway!

I know that structures and classes bundle together information. They are similar however structures are used for values whereas classes are for objects. I also know that classes are a 'reference' type. These seem to be particularly important to make code more powerful and specific.

Structures

*This time I AM going to type and code along - it's the best way of getting something ingrained into your memory after all! AND I will use 'normal' speed.

let restaurantLocation = (2, 4)
let restaurantRange = 2.5

// Pythagorean Theorem 📐🎓
func distance(from source: (x: Int, y: Int), to target: (x: Int, y: Int)) -> Double {
  let distanceX = Double(source.x - target.x)
  let distanceY = Double(source.y - target.y)
  return sqrt(distanceX * distanceX + distanceY * distanceY)
}

func isInDeliveryRange(location: (x: Int, y: Int)) -> Bool {
  let deliveryDistance = distance(from: location, to: restaurantLocation)
  return deliveryDistance < restaurantRange

}

So I did not type any of this - done already by Ray!

isInDeliveryRange(location: (x: 4, y: 3))

Right so this returns true, as it is within range of the the original location (2,4) an the range is 2.5. However, (5,4) would return false. 

The issue here is that another location and range would mean the function gets very verbose and difficult to read....

struct Location {
    
    let x: Int
    let y: Int
}
struct DeliveryArea {
    
    let centre: Location
    let radius: Double
}
let restaurantLocation = Location(x: 3, y: 4)
let restaurantRange = 2.5

let restaurantArea = DeliveryArea(centre: Location(x: 4, y: 3), radius: 7.8)

Again, Ray has gotten too technical, so I'm skimming over the next bits. 

*Interesting point here, variables are technically types of structs. This means that they are a value type - they hold the value they are given. There was the example of a = 5, b = a, then a changing value. B does NOT change value!

Challenge!

Here we go...not quite so excited as before!

// TODO: Write solution here

struct PizzaOrder {
    
    let toppings: String
    let size: String
    let base: String
}

//: Change `distance(from:to:)` to use `Location`s as parameters instead of x-y tuples.
struct Location {
  let x: Int
  let y: Int
}

// TODO: Modify this
func distance(from source: Location, to target: Location) -> Double {
  let distanceX = Double(source.x - target.x)
  let distanceY = Double(source.y - target.y)
  return sqrt(distanceX * distanceX + distanceY * distanceY)
}

//: Change `contains(_:)` to call the new `distance(from:to:)` with `Location`s.

struct DeliveryArea {
  let center: Location
  var radius: Double

  func contains(_ location: Location) -> Bool {
    // TODO: Modify this
    let distanceFromCenter =
      distance(from: center,
               to: location)

    return distanceFromCenter < radius
  }
    func overlaps(_ with: Location) -> Bool {
        
        
    }
}

OK not too bad! I've been ok with it up until the most recent bit...will see if I can get any tips for this....

OK for the pizza one, it would have been better to have had a [String] array for the toppings and I didn't realise I needed to create an instance of one. That would have been fine!

func overlaps(with area: DeliveryArea) -> Bool {
        
        return distance(from: center, to: area.center) <= radius + area.radius
        
    }
}

So I got some but not all of that. 

Protocols

These are a set of requirements that you must conform to. You need to do this to get the certain behaviour that you want. E.g. with the print statement for a protocol. Or storing to a dictionary...there are many!

Right, so many of the things in Ray's code just did not work. That's frustrating so I'm just going to move on. 

Properties

Computed properties must ALWAYS be a variable. 

struct Person {
    var firstName: String
    var lastName: String
    
    var fullName: String {
        
        return firstName + " " + lastName
    }
}

var person = Person(firstName: "Josh", lastName: "Gonet")

person.lastName = "Groban"

person.fullName

OK, so the fullName property is a stored property. I cannot input anything into it as the firstName and lastName have already been put in upon declaration of person. 

struct Person {
    var firstName: String
    var lastName: String
    
    static var outOfWedlock = "Snow"
    
    var fullName: String {
    get {
        return firstName + " " + lastName
    }
    set {
        if let spaceIndex = newValue.index(of: " ") {
            firstName = String(newValue[..<spaceIndex])
            lastName = String(newValue[newValue.index(after: spaceIndex)...])
        }
    }
    }
}

var person = Person(firstName: "Josh", lastName: "Gonet")

person.lastName = "Groban"

person.fullName

person.lastName = Person.outOfWedlock
person.fullName

OK so this is undoubtedly confusing. With the get and set, I'm not entirely sure so will see what else I can find out...

OK so something I got muddled was that firstName is a STORED property, whereas the fullName is a COMPUTED property. That makes more sense now. The fullName has to carry out some kind of code in order to get the value. 

Lazy variables - you want code to run once and only once....not sure about these yet.

Challenge!

//: Create a struct named `Temperature` that contains a stored property `degreesF` that is a `Double`, which will store the degrees in Fahrenheit. Then add a computed property called `degreesC` that is a `Double`, that calculates the degrees in Celsius. Hint: `degreesC` should be equal to `(degreesF - 32) / 1.8`.

// TODO: Write solution here

struct Temperature {
    
    var degreesF: Double {
        didSet {
            if degreesF > 100 {
                print("Too hot!")
            }
        }
    
    }
    var degreesC: Double {
        get {
           return (degreesF - 32.0) / 1.8
        }
    set {
        degreesF = (degreesC + 32) * 1.8
    
    }
}

}
//: Modify the `degreesC` computed property to add a setter, so that by setting the degrees in Celsius, it actually updates the degrees in Fahrenheit.

// TODO: Modify code above

//: Modify the `degreesF` stored property to print out "Too hot!" if it is set to above 100 degrees Fahrenheit.

// TODO: Modify code above

Not too bad again - not sure about the 'set' part for the challenge...we shall see!

One error:

 set {
        degreesF = (newValue * 1.8) + 32
    
    }

Needed the newValue keyword here!

You know what - great job! That was actually very close for the whole thing! :)

A real breakthrough in understanding here - what computed properties are!

Methods


let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]

struct SimpleDate {
    
    var month: String
    var day: Int
    
    init() {
        month = "January"
        day = 1
        
    }
    
    init(month: String, day: Int) {
        self.month = month
        self.day = day
    }
    
     mutating func advance() {
        
        day += 1
    }

    func monthsUntilWinterBreak() -> Int {
        return months.index(of: "December")! - months.index(of: month)!
    }

}

let testDate = SimpleDate(month: "February", day: 21)
testDate.monthsUntilWinterBreak()

A lot of this is familiar, which is great. The mutating keyword is needed as it is going to change the value of one of the properties (day). 

Challenge

This looks tricky! 

//: Create a structure named `Student` with three properties: first name, last name and grade. Then create a structure named `Classroom` with two properties: the class name, and an array of students. Finally, create a method named `highestGrade()` that returns the highest grade in the classroom. Try using `reduce` on the array to perform the calculation.

// TODO: Write solution here

struct Student {
    var firstName: String
    var lastName: String
    var grade: Double
}

struct Classroom {
    var className: String
    var students: [Student]
    
    func highestGrade() -> Double {
        return students.reduce(0) { result, student in
            return student.grade > result ? student.grade : result
        }
        
    }
    
}

//: Make an extension on classroom with a method named `curveGrades()`. This method should find the difference between 100 and the highest grade, and add this amount to all students scores. Finally, sort the students array so they are ordered from the students with the highest score, to the students with the lowest score.
//: 
//: **Hint**: remember that structures are value types, so if you iterate with `for student in students` you'll get a constant copy of the student, not the student inside the array. Is there another way you can loop through the students in the array?

// TODO: Write solution here
extension Classroom {
    
mutating func curveGrades() {
    
    let curveAmount = 100.0 - highestGrade()
    for i in 0..<students.count {
        students[i].grade += curveAmount
    }
    
    students.sort { student1, student 2 in
        return student1.grade > student2.grade
    }
}

}

I needed a LOT of help for most of this. It's too hard!!!

Again, it's discouraging to see myself finding lots of this stuff so difficult. Yet, I know that it's been pitched too high - there are so many conceptual leaps. Closures are mind boggling, as are a lot of the methods that Ray uses with Arrays and loops. Anyway, I've had a go and have picked up some useful nuggets. 

  • Variables are structure types - they are values that hold their own
  • Structures are VALUE types
  • They are NAMED types - they have a specific name (unlike Int, String etc.)
  • Stored properties are inputted whereas computed ones are coded in some way
  • The get/set links to the above. Computed properties have to be 'get'-ed and can have a 'set' value e.g. if the stored property gets changed. 
  • The didSet/willSet is a bit more confusing but I know that didSet means if a value has been inputted - there will be a condition and outcome attached


So it hasn't been a total waste of time! Still I just want to crack on with the next project, which will hopefully clarify a lot of the information that just boggles my mind at the moment. HOWEVER, I will complete the 'classes' series of lessons tomorrow as I simply must for a) completist sake and b) there may well be useful things there too. 

Comments

Popular posts from this blog

*Xcode Project Entry 2* F1 Quiz - part 1

Angela Yu Course Part 10 (up to lesson 112)

Angela Yu Xcode 12 Course - Part 7 (lectures 74 to 79)