Exercises For Programmers: Exercise Two – Counting Characters

This is the second exercise in Brian Hogan’s book Exercises for Programmers. My solution for this exercise is on GitHub.

Since this is still relatively early in the book, we are still dealing with some simple concepts. This exercise is about asking for a string, repeating it back to the user, and telling the user how many characters are in the string.

countCharactersSuccess

This was supposed to be the first exercise where you set up a user interface, but I jumped the gun on that by setting one up with the first exercise.

The only changes between the first exercise and the second were that there was going to be another label to output the number of characters and that I needed to count the characters in the string.

I was able to complete this far more quickly than the first exercise because most of the road blocks I encountered with the first exercise were solved for the second. I already knew how to set up my auto layout. I remembered how to get the text from the UITextField. I already had most of my set up for the processing I needed to do for the labels.

Failure condition. Need to set both labels, including making sure the second label is an empty string.

Failure condition. Need to set both labels, including making sure the second label is an empty string.

I started out thinking that because I had two labels I needed to create two separate functions. I initially made a variable out of the text field input and passed that into my two functions. Then I thought more critically about it.

I was passing the same information into both functions and doing the same “if-else if-else” statement:

func yourInputString(input:String) -> String {
    
    if input.characters.count == 0 {
        return "You need to enter a string!"
    } else if Int(input) != nil {
        return "Please enter letters and not numbers!"
    } else {
        return "Your input string: (input)"
    }
    
}

func numberOfCharactersInString(input:String) -> String {
    
    if input.characters.count == 0 {
        return ""
    } else if Int(input) != nil {
        return ""
    } else {
        return "(input.uppercaseString) has (input.characters.count) characters."
    }
    
}

That is a lot of repeated code. There is probably a better way to consolidate this.

One of the things Brad and I talked about with Swift having an advantage over Objective-C is that it can return more than one thing. One reason that the NSError stuff is so screwy is because you can’t return more than one thing. You have to pass a pointer to the location in memory for the error because you can’t simply return the error.

I realized that I could cut down on a lot of code by simply returning a tuple that contained both label strings. This cut down on code in both my helper functions and in my main View Controller because I only had to call one function and then assign the result from that function to the labels in the UI.

func inputStringAndCharacterCount(input:String) -> (String, String) {
    
    let yourInputString:String
    let numberOfCharactersString:String
    
    if input.characters.count == 0 {
        yourInputString = "You need to enter a string!"
        numberOfCharactersString = ""
    } else if Int(input) != nil {
        yourInputString = "Please enter letters and not numbers!"
        numberOfCharactersString = ""
    } else {
        yourInputString = "Your input string: (input)"
        numberOfCharactersString = "(input.uppercaseString) has (input.characters.count) characters."
    }
    
    return (yourInputString, numberOfCharactersString)
}

I am finding this iterative approach of starting with something simple then adding complexity is a really good way to approach programming. You take lessons you learned doing something simple and you apply them as you do more and more complicated things.

Writing unit tests also forces you to think in ways to make it as easy as possible for yourself to be able to separate out as much functionality as possible in a way for it to be testable.

When Brad was telling me initially about using tuples I didn’t get how he came up with the solution. It was obvious once I saw it, but I didn’t get how he came up with it. I get it now. When I was writing my code and saw that I was repeating myself a lot, it forced me to think more critically about how I could make my code better.

I am happy I am doing these exercises because I don’t feel I get to code as much as I should. I am now seeing that coding things for myself helps me to learn things better than just being told what to do by someone who learned the way I am learning now.

We can read all the books we want to on clean code and so forth, but you really learn by doing and making mistakes and going back to refactor. I think a lot of people never go back and refactor and that is unfortunate for them. They are missing out on a great learning opportunity.