iOS 9 Stanford Course in Swift - Lecture 1 - ios

I'm currently trying to complete the Swift Course on iTunes U and we are building a calculator. I'm having trouble understanding part of the code.
I added the code below that I thought was relevant from the file.
Here is what confuses me: why does operation(operand) compute the value for the UnaryOperation (i.e. the square root)? I see that when the CalculatorBrain class is called the dictionary is initialized, but when I print the dictionary out I just get something that looks like this: [✕: ✕, -: -, +: +, ⌹: ⌹, √: √]. So where/when does the program compute the square root when I click on the square root button?
Class CalculatorBrain
{
private enum Op: Printable
{
case Operand(Double)
case UnaryOperation(String, Double -> Double)
case BinaryOperation(String, (Double, Double) -> Double)
var description: String {
get {
switch self {
case .Operand(let operand):
return "\(operand)"
case .UnaryOperation(let symbol, _):
return symbol
case .BinaryOperation(let symbol, _):
return symbol
}
}
}
}
private var opStack = [Op]()
private var knownOps = [String: Op]()
init() {
func learnOp(op: Op) {
knownOps[op.description] = op
}
learnOp(Op.BinaryOperation("✕", *))
learnOp(Op.BinaryOperation("⌹") { $1 / $0 })
learnOp(Op.BinaryOperation("+", +))
learnOp(Op.BinaryOperation("-") { $0 - $1 })
learnOp(Op.UnaryOperation ("√", sqrt))
}
private func evaluate(ops: [Op]) -> (result: Double?, remainingOps: [Op])
{
if !ops.isEmpty {
var remainingOps = ops
let op = remainingOps.removeLast()
switch op {
case .Operand(let operand):
return (operand, remainingOps)
case .UnaryOperation(_, let operation):
let operandEvaluation = evaluate(remainingOps)
if let operand = operandEvaluation.result {
**return (operation(operand), operandEvaluation.remainingOps)**
}
// case.BinaryOperation(.....)
}
}
return (nil, ops)
}
func evaluate() -> Double? {
let (result, remainder) = evaluate(opStack)
return result
}
func pushOperand(operand: Double) -> Double? {
opStack.append(Op.Operand(operand))
return evaluate()
}
func performOperation(symbol: String) -> Double? {
if let operation = knownOps[symbol] {
opStack.append(operation)
}
return evaluate()
}
}

The Op enum implements the Printable protocol, which means it has a description: String property. When you print the Dictionary, you are sending [String : Op] to the println function which then tries to print the Op using its description.
The reason the description of the operators is the same as its key in the Dictionary is because the learnOp(op: Op) function sets the key to be op.description (knownOps[op.description] = op)
To see the effects of this, you could add a new operator learnOp(Op.UnaryOperation ("#", sqrt)) which will be printed as #:# inside of the knownOps Dictionary. (And if you add a new button for the # operator, it will also perform the square root operation)
Since the calculator is stack based, the operands get pushed on, then the operations. When evaluate() gets called, it calls evaluate(opStack) passing the entire stack through.
evaluate(ops: [Op]) then takes the to item off of the stack and evaluates the function after having calculated the operands.
As an example, lets say you want to calucalte sqrt(4 + 5).
You would push the items onto the stack, and it would look like: [ 4, 5, +, sqrt ]
Then evaluate(ops: [Op]) sees the sqrt and evaluates the operand with a recursive call. That call then evaluates + with two more recursive calls which return 5 and 4.
The tree of calls would look like this:
ops: [4, 5, +, sqrt] // Returns sqrt(9) = 3
|
ops: [4, 5, +] // Returns 4 + 5 = 9
____|_____
| |
ops: [4, 5] ops: [4]
return 5 return 4
I strongly recommend you put a breakpoint on the evaluate() -> Double? function and step through the program to see where it goes with different operands and operations.

learnOp(Op.UnaryOperation ("√", sqrt))
sqrt is a built in function, so you're teaching the calculator that "√" means it should perform the sqrt operation.

Related

How to use firstIndex in Switft to find all results

I am trying to split a string into an array of letters, but keep some of the letters together. (I'm trying to break them into sound groups for pronunciation, for example).
So, for example, all the "sh' combinations would be one value in the array instead of two.
It is easy to find an 's' in an array that I know has an "sh" in it, using firstIndex. But how do I get more than just the first, or last, index of the array?
The Swift documentation includes this example:
let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
if let i = students.firstIndex(where: { $0.hasPrefix("A") }) {
print("\(students[i]) starts with 'A'!")
}
// Prints "Abena starts with 'A'!"
How do I get both Abena and Akosua (and others, if there were more?)
Here is my code that accomplishes some of what I want (please excuse the rather lame error catching)
let message = "she sells seashells"
var letterArray = message.map { String($0)}
var error = false
while error == false {
if message.contains("sh") {
guard let locate1 = letterArray.firstIndex(of: "s") else{
error = true
break }
let locate2 = locate1 + 1
//since it keeps finding an s it doesn't know how to move on to rest of string and we get an infinite loop
if letterArray[locate2] == "h"{
letterArray.insert("sh", at: locate1)
letterArray.remove (at: locate1 + 1)
letterArray.remove (at: locate2)}}
else { error = true }}
print (message, letterArray)
Instead of first use filter you will get both Abena and Akosua (and others, if there were more?)
extension Array where Element: Equatable {
func allIndexes(of element: Element) -> [Int] {
return self.enumerated().filter({ element == $0.element }).map({ $0.offset })
}
}
You can then call
letterArray.allIndexes(of: "s") // [0, 4, 8, 10, 13, 18]
You can filter the collection indices:
let students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
let indices = students.indices.filter({students[$0].hasPrefix("A")})
print(indices) // "[1, 4]\n"
You can also create your own indices method that takes a predicate:
extension Collection {
func indices(where predicate: #escaping (Element) throws -> Bool) rethrows -> [Index] {
try indices.filter { try predicate(self[$0]) }
}
}
Usage:
let indices = students.indices { $0.hasPrefix("A") }
print(indices) // "[1, 4]\n"
or indices(of:) where the collection elements are Equatable:
extension Collection where Element: Equatable {
func indices(of element: Element) -> [Index] {
indices.filter { self[$0] == element }
}
}
usage:
let message = "she sells seashells"
let indices = message.indices(of: "s")
print(indices)
Note: If you need to find all ranges of a substring in a string you can check this post.
Have fun!
["Kofi", "Abena", "Peter", "Kweku", "Akosua"].forEach {
if $0.hasPrefix("A") {
print("\($0) starts with 'A'!")
}
}
If you really want to use the firstIndex method, here's a recursive(!) implementation just for fun :D
extension Collection where Element: Equatable {
/// Returns the indices of an element from the specified index to the end of the collection.
func indices(of element: Element, fromIndex: Index? = nil) -> [Index] {
let subsequence = suffix(from: fromIndex ?? startIndex)
if let elementIndex = subsequence.firstIndex(of: element) {
return [elementIndex] + indices(of: element, fromIndex: index(elementIndex, offsetBy: 1))
}
return []
}
}
Recursions
Given n instances of element in the collection, the function will be called n+1 times (including the first call).
Complexity
Looking at complexity, suffix(from:) is O(1), and firstIndex(of:) is O(n). Assuming that firstIndex terminates once it encounters the first match, any recursions simply pick up where we left off. Therefore, indices(of:fromIndex:) is O(n), just as good as using filter. Sadly, this function is not tail recursive... although we can change that by keeping a running total.
Performance
[Maybe I'll do this another time.]
Disclaimer
Recursion is fun and all, but you should probably use Leo Dabus' solution.

Have a variable with multiple types in Swift

I would like to have a variable, which can have multiple types (only ones, I defined), like:
var example: String, Int = 0
example = "hi"
This variable should be able to hold only values of type Int and String.
Is this possible?
Thanks for your help ;)
An “enumeration with associated value” might be what you are looking for:
enum StringOrInt {
case string(String)
case int(Int)
}
You can either assign a string or an integer:
var value: StringOrInt
value = .string("Hello")
// ...
value = .int(123)
Retrieving the contents is done with a switch-statement:
switch value {
case .string(let s): print("String:", s)
case .int(let n): print("Int:", n)
}
If you declare conformance to the Equatable protocol then
you can also check values for equality:
enum StringOrInt: Equatable {
case string(String)
case int(Int)
}
let v = StringOrInt.string("Hi")
let w = StringOrInt.int(0)
if v == w { ... }
Here is how you can achieve it. Works exactly how you'd expect.
protocol StringOrInt { }
extension Int: StringOrInt { }
extension String: StringOrInt { }
var a: StringOrInt = "10"
a = 10 //> 10
a = "q" //> "q"
a = 0.8 //> Error
NB! I would not suggest you to use it in production code. It might be confusing for your teammates.
UPD: as #Martin R mentioned: Note that this restricts the possible types only “by convention.” Any module (or source file) can add a extension MyType: StringOrInt { } conformance.
No, this is not possible for classes, structs, etc.
But it is possible for protocols.
You can this:
protocol Walker {
func go()
}
protocol Sleeper {
func sleep()
}
var ab = Walker & Sleeper
or even
struct Person {
var name: String
}
var ab = Person & Walker & Sleeper
But I don't recomment use this way.
More useful this:
struct Person: Walker, Sleeper {
/// code
}
var ab = Person
You can use Tuple.
Example:
let example: (String, Int) = ("hi", 0)
And access each data by index:
let stringFromExampleTuple = example.0 // "hi"
let intFromtExampleTuple = example.1 // 0

Return statement skipped in Swift

I'm trying to teach myself Swift via the Stanford iTunes U course (currently working on the calculator app), but I just ran into a super weird issue that I've never seen before and can't figure out how to solve: one of my return statements (and only the return statement itself) is being skipped during runtime.
func evaluateOps(ops: [Op]) -> (result: Double?, remainingOps: [Op]) {
if !ops.isEmpty {
var remainingOps = ops
let op = remainingOps.removeLast()
switch op {
case ...
case ...
case .BinaryOperation(_, let operation):
// check that there are 2 operands before it
if remainingOps.count == 2 {
let op1Evaluation = evaluateOps(remainingOps)
if let operand1 = op1Evaluation.result {
let op2Evaluation = evaluateOps(op1Evaluation.remainingOps)
if let operand2 = op2Evaluation.result {
// PROBLEM AREA...
let x = (operation(operand1, operand2), op2Evaluation.remainingOps)
println("results: \(x.0) remainder: \(x.1)")
return (x.0, x.1) // skipped during runtime...
}
}
}
else { ... }
}
}
println("returning nil")
return (nil, ops)
}
// troublesome method is called here...
let (result, remainder) = evaluateOps(opStack)
println("\(opStrings) = \(result) with \(remainderStrings) leftover")
Everything works so that, if I tried to calculate 5*3 for example, the console would read:
results: 15.0 remainder: []
returning nil
[5.0, ×, 3.0] = nil with [5.0, ×, 3.0] leftover
I think the problem might have something to do with the fact that, in the above code, if I tried to simply return x, I get a compile error that reads Cannot express tuple conversion '(Double, [CalculatorModel.Op])' to '(result: Double?, remainingOps: [CalculatorModel.Op])'. I also have no idea what to do with this.
In my researching the problem, I've discovered the downcasting keyword as (see altered code below), which removed the compile error when returning x rather than (x.0, x.1) but results in the same console result (except that now it says results: Optional(15.0) remainder: []):
let x = (operation(operand1, operand2) as Double?, op2Evaluation.remainingOps as [Op])
println("results: \(x.0) remainder: \(x.1)")
return x // no error but same result as return (x.0, x.1)
I've also tried sticking a println(getClassName(x.0!)) just before the return statement to make sure I even had a double, and it spit out __NSCFNumber... which I also researched, and found to be highly underdocumented (or at least not documented well enough to figure out how that's affecting me or how I can fix it!)
Although as you can see, it should be a double...
enum Op {
case Operand(Double)
case UnaryOperation(String, Double -> Double)
case BinaryOperation(String, (Double, Double) -> Double)
var description: String {
get {
switch self {
case .Operand(let operand):
return "\(operand)"
case .UnaryOperation(let symbol, _):
return "\(symbol)"
case .BinaryOperation(let symbol, _):
return "\(symbol)"
}
}
}
}
As you can see, everything's working perfectly fine as it's performing the calculation and everything...until it gets to that little troublesome area. I've searched StackOverflow and did a bit of Googling, but this seems to be a rather uncommon problem... Any help is appreciated! Let me know if you need any further information. Thanks!
EDIT:
getClassName(_:) is defined below (found it online somewhere...):
func getClassName(obj : AnyObject) -> String
{
let objectClass : AnyClass! = object_getClass(obj)
let className = objectClass.description()
return className
}

Counting number of Arrays that contain the same two values

Given a Dictionary<String, Arrary<Int>> find the how many entries have the same two specified values in the first 5 entries in the Array<Int>.
For example:
Given:
let numberSeries = [
"20022016": [07,14,36,47,50,02,05],
"13022016": [16,07,32,36,41,07,09],
"27022016": [14,18,19,31,36,04,05],
]
And the values: 7 and 36, the result should be 2 since the first and second entry have both the values 7 and 36 in the first 5 entries of the entry's array.
I've tried to accomplish this many ways, but I haven't been able to get it to work.
This is my current attempt:
//created a dictionary with (key, values)
let numberSeries = [
"20022016": [07,14,36,47,50,02,05],
"13022016": [16,07,32,36,41,07,09],
"27022016": [14,18,19,31,36,04,05],
]
var a = 07 //number to look for
var b = 36 // number to look for
// SearchForPairAB // search for pair // Doesn't Work.
var ab = [a,b] // pair to look for
var abPairApearedCount = 0
for (kind, numbers) in numberSeries {
for number in numbers[0...4] {
if number == ab { //err: Cannot invoke '==' with argument listof type Int, #Value [Int]
abPairApearedCount++
}
}
}
This gives the error: Cannot invoke '==' with argument listof type Int, #Value [Int] on the line: if number == ab
You can't use == to compare an Int and Array<Int>, that just doesn't make any sense from a comparison perspective. There are lots of different ways you can achieve what you're trying to do though. In this case I'd probably use map/reduce to count your pairs.
The idea is to map the values in your ab array to Bool values determined by whether or not the value is in your numbers array. Then, reduce those mapped Bools to a single value: true if they're all true, or false. If that reduced value is true, then we found the pair so we increment the count.
var ab = [a,b] // pair to look for
var abPairApearedCount = 0
for (kind, numbers) in numberSeries {
let found = ab.map({ number in
// find is a built-in function that returns the index of the value
// in the array, or nil if it's not found
return find(numbers[0...4], number) != nil
}).reduce(true) { (result, value: Bool) in
return result && value
}
if found {
abPairApearedCount++
}
}
That can actually be compacted quite a bit by using some of Swift's more concise syntax:
var ab = [a,b] // pair to look for
var abPairApearedCount = 0
for (kind, numbers) in numberSeries {
let found = ab.map({ find(numbers[0...4], $0) != nil }).reduce(true) { $0 && $1 }
if found {
abPairApearedCount++
}
}
And, just for fun, can be compacted even further by using reduce instead of a for-in loop:
var ab = [a,b] // pair to look for
var abPairApearedCount = reduce(numberSeries, 0) { result, series in
result + (ab.map({ find(series.1[0...4], $0) != nil }).reduce(true) { $0 && $1 } ? 1 : 0)
}
That's getting fairly unreadable though, so I'd probably expand some of that back out.
So here's my FP solution, aimed at decomposing the problem into easily digestible and reusable bite-sized chunks:
First, we define a functor that trims an array to a given length:
func trimLength<T>(length: Int) -> ([T]) -> [T] {
return { return Array($0[0...length]) }
}
Using this we can trim all the elements using map(array, trimLength(5))
Now, we need an predicate to determine if all the elements of one array are in the target array:
func containsAll<T:Equatable>(check:[T]) -> ([T]) -> Bool {
return { target in
return reduce(check, true, { acc, elem in return acc && contains(target, elem) })
}
}
This is the ugliest bit of code here, but essentially it's just iterating over check and insuring that each element is in the target array. Once we've got this we can use filter(array, containsAll([7, 26])) to eliminate all elements of the array that don't contain all of our target values.
At this point, we can glue the whole thing together as:
filter(map(numberSeries.values, trimLength(5)), containsAll([7, 36])).count
But long lines of nested functions are hard to read, let's define a couple of helper functions and a custom operator:
func rmap<S:SequenceType, T>(transform:(S.Generator.Element)->T) -> (S) -> [T] {
return { return map($0, transform) }
}
func rfilter<S:SequenceType>(predicate:(S.Generator.Element)->Bool) -> (S) -> [S.Generator.Element] {
return { sequence in return filter(sequence, predicate) }
}
infix operator <^> { associativity left }
func <^> <S, T>(left:S, right:(S)->T) -> T {
return right(left)
}
And a convenience function to count it's inputs:
func count<T>(array:[T]) -> Int {
return array.count
}
Now we can condense the whole thing as:
numberSeries.values <^> rmap(trimLength(5)) <^> rfilter(containsAll([7, 36])) <^> count

Passing func as param Swift

I am moving through the Swift Programming Language Book and I don't fully understand this:
//I don't understand 'condition: Int -> Bool' as a parameter, what is that saying?
func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
//So here i see we are iterating through an array of integers
for item in list {
//I see that condition should return a Bool, but what exactly is being compared here? Is it, 'if item is an Int return true'??
if condition(item) {
return true
}
}
return false
}
//This func I understand
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 20]
hasAnyMatches(numbers, lessThanTen)
If you could explain a little more of what exactly is going on here it would be much appreciated. I ask most of the question in the comments so it's easier to read but the main thing confusing me is condition: Int -> Bool as a parameter.
As it states in the book, the second argument is a function (what's actually going on I've explained in code comments)
// 'condition: Int -> Bool' is saying that it accepts a function that takes an Int and returns a Bool
func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
// Takes each item in array in turn
for item in list {
// Here each 'item' in the list is sent to the lessThanTen function to test whether the item is less than 10 and returns true if it is the case and executes the code, which in turn returns true
if condition(item) {
return true
}
}
return false
}
// This function is sent as the second argument and then called from hasAnyMatches
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 20]
hasAnyMatches(numbers, lessThanTen)
In the scenario provided, the loop will continue to run until it hits 7, at which point true will be returned. If there was no number beneath 10 then the function would return false.
condition: Int -> Bool is the syntax for you to pass in a closure, more commonly known as functions.
The function lessThanTen has a type of Int -> Bool, as can be seen from its signature
inputTypes->outputType is basically all you need to define a function!
It should work like this as well:
hasAnyMatches(numbers, { number in ; return number < 10 } )
// Or like this, with trailing closure syntax
hasAnyMatches(numbers) {
number in
return number < 10
}

Resources