Swift: Using String Ranges The Functional Way
A few weeks ago, I wrote about How To Find A Substring In Range of a Swift String. At the time, it seemed very odd why Ranges were so hard to work with in Swift, but at least I found a way to work around them…
However, as I’ve been learning a lot more about Functional Programming in the past few weeks, I’ve come to realize that Ranges are created this way to help guide us to use them in a more specific way – particularly the more functional way.
One of the very first thing I learned from watching the edX FP101x lectures was the concept of head and tail. In fact, this is mentioned 5 minutes into the very first lecture, and the concept comes up over and over again! The idea is very simple. The head of a list is it’s first element, and the tail is an list of all elements except the first one (so basically it drops the first element from the list).
head[1,2,3,4,5] // 1 tail[1,2,3,4,5] // [2,3,4,5]
In functional programming, instead of using the typical for-loops to solve problems, the use of head, tail, and recursion is favored to deal with elements in a list one at a time.
So let’s say you want to get the first x number of characters from a word. Without thinking of Swift Ranges (and they’re hard to work with anyway!), you might initially approach it something like this:
func getSubstringUpToIndex(index: Int, fromString str: String) -> String { var substring = "" for (i, letter) in enumerate(str) { substring.append(letter) if i == index - 1 { break } } return substring } getSubstringUpToIndex(5, fromString: "Hello, NatashaTheRobot") // Hello
Now let’s do the same thing the functional way – with head, tail, and recursion:
func getSubstringUpToIndex(index: Int, fromString str: String) -> String { let (head, tail) = (str[str.startIndex], dropFirst(str)) if index == 1 { return String(head) } return String(head) + getSubstringUpToIndex(index - 1, fromString: tail) } getSubstringUpToIndex(5, fromString: "Hello, NatashaTheRobot") // Hello
So hopefully now it makes sense why Swift Ranges have a startIndex and endIndex available with no easy way to go in between!
Of course in this case you can solve this problem the way I mentioned in my initial post about Swift Ranges, but in solving it this way I’m hoping you (and I!) can start thinking a lot more in this functional way of going through the list one at a time in favor of for loops. After all, this focus on first and last (with no easy way to go in between) is apparent in many other Swift APIs…