Are you a Swift Ninja?
Welcome back to our “Are you a Swift Ninja?” Programming Challenge!
In the first part of this series, you got some practice with default values in functions, variadic parameters, map/reduce, advanced switch statement features, and more.
Hopefully, you earned plenty of shurikens along the way!
In this second and final part of the series, you will get 4 more challenges to test your ninja skills.
In addition, this tutorial has a special final challenge, where you will get a chance to compete against other developers for fame and fortune!
The best solution to the final challenge will be featured in this post, and will also get a free copy of our upcoming Swift by Tutorials Bundle, which includes three books about programming in Swift.
Ninja mode activate – the challenge continues!
Challenge #5
Stretch those fingers and assume the position. It’s time to do another problem that involves recursion and function syntax.
Write a single function that reverses the text in a string. For example, when passed the string “Marin Todorov” will return the string “vorodoT niraM”.
Requirements:
- You can’t use any loop operators nor subscripts (i.e. no square brackets in the code).
- You can’t use any built-in
Array
functions.
- Don’t use variables.
Here’s an example of a function call and its output:
reverseString("Marin Todorov") //--> "vorodoT niraM" |
Solution Inside: Hints |
SelectShow> |
Use recursion to move all letters one by one from the first string to another, effectively reversing their order.
|
Solution Inside: Tutorial |
SelectShow> |
This problem has a solution very similar to the one of Problem #4. The difference is mostly due to the fact that the function reverseString you need to write takes one parameter while countFrom(from:, to:) takes two. You can easily skip around that fact by using what you’ve learned so far!
Start by defining a function that takes two parameters, the latter being an empty string by default. You’ll use this second parameter to accumulate the result of the function.
Every recursive call to your function will move one character from the input string input to the result string result — reversing the order characters appear in the text.
When there are no more characters in the input text, that means you’ve moved them all to the result, so you need to stop recursing.
Want to see it? Here’s the complete solution:
func reverseString (input: String, output: String="") -> String {
if input.isEmpty {
return output
} else {
return reverseString(
input.substringToIndex(input.endIndex.predecessor()),
output: output + input.substringFromIndex(input.endIndex.predecessor()))
}
}
} |
First you check if input is empty – this is your stop condition. If yes, then just return output with the accumulated result.
If input still has characters, then remove the last one from input and add that character to output . Call the function recursively with these new input and output parameters.
To better understand how the function works look at the parameters of each recursive call:
(Marin Todorov, )
(Marin Todoro, v)
(Marin Todor, vo)
(Marin Todo, vor)
(Marin Tod, voro)
(Marin To, vorod)
(Marin T, vorodo)
(Marin , vorodoT)
(Marin, vorodoT )
(Mari, vorodoT n)
(Mar, vorodoT ni)
(Ma, vorodoT nir)
(M, vorodoT nira)
(, vorodoT niraM) |
Give yourself for trying out the code in a Playground and producing the text output from above. Note: you’ll need to add a println() somewhere.
|
Challenge #6
Your next challenge has to do with operator overloading — one of the most powerful features of Swift. I hope you’ve had a chance to look into how to do that :]
Your challenge is to overload the “*
” operator so it takes a Character
and an Int
and produces a String
with the character repeated Int times.
Here’s an example usage and output:
"-" * 10 //output is: "----------" |
Make usage of everything you learned so far and don’t use any variables, loops, inout parameters, or subscripts.
You might need to define an extra auxiliary function. At the time of writing, Xcode crashes when you try to define a nested function inside an operator overload. Hopefully this is corrected at some point.
Solution Inside: Hints |
SelectShow> |
Write a separate function that takes 3 parameters: the input character, the accumulated string result, and the times to repeat the character. Call this function recursively as you did in the previous challenge until the length of the result equals the target length.
|
Solution Inside: Tutorial |
SelectShow> |
For this solution, write a recursive function that takes the following parameters: a Character , a String to accumulate the result, and a Double to set the desired length of the resulting string:
func charMult(char: Character, result: String, length: Double) -> String {
if Int64(countElements(result)) < Int64(length) {
return charMult(char, result+char, length)
} else {
return result
}
} |
This function is very similar to what you developed in the previous challenge. It keeps recursively calling itself and adding one more character to the accumulator until the length of the result equals the given length parameter.
When you reach the target length, you just return the result.
In the function above, you need to define the length parameter as a Double because when you write an integer constant in your code (i.e. 10, 4, or 2014) Swift will by default cast it to a Double value.
In order to have your “* ” operator work for on a character and an integer like this:
"x" * 20
… you have to make it work for a Character on the left side and a Double on the right side. It may feel a bit strange, but you’ll soon find it’s part of what makes Swift very powerful.
Finally, overload the operator itself and make it use your charMult(char: Character, result: String, length: Double) function:
@infix func * (left: Character, right: Double) -> String {
return charMult(left, "", right)
} |
You declare a function with the operator itself as the name (func * ) and declare it to be an @infix . Infix functions take their parameters from their left and right side instead of having them in a list surround by brackets.
You declare that the left side of “* ” should be a Character and right side a Double , and that the result of the operation is a String . You declare all this as you would declare any normal function.
For the body of the operator overload, you just call charMult(char: Character, result: String, length: Double) and return the result.
Give the new operator a try in a Playground, try fun things like these:
"-" * 10 + ">" //prints an arrow
"%" + "~" * 6 //prints a worm
"Z" * 20 //gets sleeping |
Give yourself for trying the above examples in a Playground.
|
Challenge #7
This challenge, while not necessarily pushing you to write beautiful and optimized code, will lead you to discover (or exercise) another very powerful feature of Swift.
“What’s that?” you might ask. Well, you’ll just have to work through it and figure that out for yourself!
For this challenge you’ll need to use this function:
import Foundation
func doWork() -> Bool {
return arc4random() % 10 > 5
} |
This function, for the purpose of writing and testing your solution, randomly succeeds or fails (eg. returns true
or false
).
Write code (and/or additional functions) that will output the message “success!” when doWork()
returns true
, and will output “error” when doWork()
returns false
. Your solution should meet the following requirements:
- You can’t modify the source of
doWork()
.
- You can’t use
if
, switch
, while
, or let
.
- You can’t use the ternary operator
?:
.
Solution Inside: Hints |
SelectShow> |
Use the fact that doWork() returns a boolean result, and therefore may be used in a logical expression.
Logical expressions can be used on a separate line of code in a playground, and their result will show up in the output area.
|
Solution Inside: Tutorial |
SelectShow> |
To solve this problem, use a logical expression instead of a control structure like if or switch .
Modern languages don’t evaluate parts of a logical expression that don’t change the result of the expression as a whole. For example, if the result of the expression is already clear halfway through evaluating it, the rest is just ignored.
What does that really mean?
Consider this expression:
true && true && false && true & true |
Let’s start evaluating the values two by two from left to right:
//evaluate first two elements
true && true -> true
//evaluate result so far + third element
true && false -> false |
At this point the result is false . The runtime can look ahead and see that only “&&” operators follow, and since the current result is false - – the result could never be true .
At this point, the runtime stops evaluating the expression and simply takes false as the result.
Since you now understand the basic concept of control flow via expressions, have a look at the solution to the original problem:
func reportSuccess() -> Bool {
println("success!")
return true
}
func reportError() -> Bool {
println("error")
return true
}
doWork() && reportSuccess() || reportError() |
You declare two functions: one prints “success!” and one prints “error”. Let’s have a look at what happens when doWork() returns either boolean value:
When doWork() returns true
Evaluate the expression in the following order:
doWork() returns true , so there’s a chance for “&&” to result in either false or true depending on the value of reportSuccess() .
reportSuccess() is evaluated and prints “success!”.
- The result of
doWork() && reportSuccess() is true .
- Follows an || operator, but since its left side is already
true the value of the right side does not affect the end result, so it’s never evaluated.
When doWork() returns false
Evaluate the expression with the following steps:
doWork() returns false , so the result of “&&” is already false . reportSuccess() is never evaluated.
- Since the result so far is
false the right side of the || has chance to turn the result into true so it gets evaluated.
- When evaluated
reportError() prints out “error”.
Note: If you are interested in how lazy evaluation of logical expressions really works you can have a look at this post on the Apple Swift blog that came out during the editing phase of this article.
|
Challenge #8
Currying is a relatively unexplored area outside of functional languages like ML, SML, and Haskel. But as you may have noticed from the presentations at WWDC, Swift has this feature as well — and the Apple engineers seem to be pretty excited about it.
Are you up to the challenge of using currying and partial function application?
Extend the Array
structure and add 3 new functions that you could call like this on an array of any type:
list.swapElementAtIndex(index: Int)(withIndex: Int)
: Returns a copy of the original array with the elements at indexes index
and withIndex
exchanged.
list.arrayWithElementAtIndexToFront(index: Int)
: Returns a copy of the original array with the element at index index
exchanged with the first element .
list.arrayWithElementAtIndexToBack(index: Int)
: Returns a copy of the original array with the element at index index
exchanged with the last element.
(The examples above use an array called list
).
Requirements:
- You can use the keyword
func
only one time – to declare swapElementAtIndex
.
Here is an example of usage and its output:
let list = [1, 4, 5, 6, 20, 50] //--> [1, 4, 5, 6, 20, 50]
list.arrayWithElementAtIndexToBack(2) //--> [1, 4, 50, 6, 20, 5]
list.arrayWithElementAtIndexToFront(4) //--> [20, 4, 5, 6, 1, 50] |
Solution Inside: Hints |
SelectShow> |
Your swapElementAtIndex function needs to take a single Int parameter, and to return another function. The second function also takes in a single Int parameter and can use the two parameters to swap the elements in the array at the given indexes.
Since arrayWithElementAtIndexToFront always swaps an element to the one at index 0 you can pre-fabricate a function that provides the parameter to swapElementAtIndex – it’s always 0!
|
Solution Inside: Tutorial |
SelectShow> |
Let’s start by defining swapElementAtIndex .
As you can see from the requirements, the function takes one Int parameter, and its result is a function that also takes one Int parameter. Define this by simply chaining “-> ” in the function definition, like so:
extension Array {
func swapElementAtIndex(index: Int) -> (Int) -> Array {
//code
}
} |
The code above declares swapElementAtIndex(index:Int) , which returns a function with one Int param — and returns an array of the same type as the original array Array .
So, how do you return a function from swapElementAtIndex(index:Int) ? Sure, you could define a nested function and return it, but there’s a more elegant solution.
Try just returning a closure with the same number of parameters as the expected function.
In the example above, replace the comment “//code ” with:
return { withIndex in
var result = self
if index < self.count && withIndex < self.count {
(result[index], result[withIndex]) = (result[withIndex], result[index])
}
return result
} |
The closure takes one parameter withIndex and swaps the elements at indexes index and withIndex . You’ve already done this several times by now, so it should feel like second nature by now :]
To be able to modify the contents of the array you need to get a mutable copy of it. To get one just declare a local variable and the copy is automatically mutable due to the use of the var keyword.
The next step is to declare the rest of the required functions. But you can’t use func anymore…
Don’t furrow your brow — class and structure variables can be functions too, so you can skip using the keyword func and use var instead.
You’re going to pre-fabricate two variables to call swapElementAtIndex(index:Int) .
Add inside the extension code:
var arrayWithElementAtIndexToFront: (Int) -> Array {
return swapElementAtIndex(0 as Int)
}
var arrayWithElementAtIndexToBack: (Int) -> Array {
return swapElementAtIndex((self.count-1) as Int)
} |
What these two new functions do is pre-fill one of the parameters required to call swapElementAtIndex(index: Int)(withIndex: Int) (therefore the term “partial function application”).
And that’s all it takes for this solution.
You learned how to create a curried function, and then how to prefabricate other functions out of it using partial application. Good job!
|
The Final Challenge
Time for the last challenge. This time there will be no hints and no tutorial. It’s all on you, dear Ninja.
Approach this challenge carefully and design a beautiful solution. Then post your solution in the comments on this post. Note it’s ideal if you post your solution as a gist so it has nice syntax highlighting.
I will select one solution as the winner. The winner will be immortalized in this post as the correct solution for this challenge! Remember to leave your name along the code, so you can live in infamy, forever known as a true Swift Ninja :]
In addition, the winner will will receive a a free copy of our upcoming Swift by Tutorials Bundle, which includes three books about programming in Swift!
In choosing a winner, I will consider correctness, brevity and use of Swift’s language features. Embrace all the techniques you’ve explored in this post. Should you and another developer post the same winning solution, I’ll choose the one that was posted first.
You have 2 weeks from the time this post goes live. Better get to it!
Let’s get to coding! Here’s the enumerations and a struct to get started:
enum Suit {
case Clubs, Diamonds, Hearts, Spades
}
enum Rank {
case Jack, Queen, King, Ace
case Num(Int)
}
struct Card {
let suit: Suit
let rank: Rank
} |
Write a function called countHand
that takes in an array of Card
instances and counts the total value of the cards given.
The requirements for your solution are as follows:
- The function returns the value of the cards in the hand as an Int.
- Does not use loops or nested functions.
- The card values are as follows:
- Any Ace preceded by 5 of Diamonds is worth 100 points.
- Any odd numeric card (3, 5, 7, 9) of any suit’s worth the double of its rank value in points when immediately preceded in the hand by any card of the Hearts. Examples:
- The hand 3♥, 7♣ has total value of 14.
- The hand 3♣, 7♥ has total value of 0.
- Since brevity is one of the points on which your code will be assessed, consider using one statement functions, closures and case statements.
Here’s an example of usage and its result. Use this to check your solution:
countHand([
Card(suit:Suit.Hearts, rank:Rank.Num(10)),
Card(suit:Suit.Hearts, rank:Rank.Num(6)),
Card(suit:Suit.Diamonds, rank:Rank.Num(5)),
Card(suit:Suit.Clubs, rank:Rank.Ace),
Card(suit:Suit.Diamonds, rank:Rank.Jack)
]) //--> 110 |
Where to Go From Here
First, check your challenge result!
Calculate how many shurikens you earned — for the final challenge, give yourself 3 shurikens if you solved the problem and none if you didn’t.
How ninja are you?
- 23 or more shurikens: Congratulations! You’re a Swift ninja! Fantastic!
- 16 to 22 shurikens: You’re doing well, but you’ll benefit from delving into the nitty gritty details of Swift. Luckily, this website is a great resource to learn more about Swift.
- 15 or less shurikens: Though you may not have been able to beat the challenges without help, you certainly learned tons of new Swift techniques and are on your way to being a certifiable ninja. Props!
You can download the complete Playground solution to the first 8 challenges here: NinjaChallenge-Completed.playground
Even if you mastered the challenges, you can always learn more! Check out some additional Swift resources:
Remember to post your solution to the final challenge and your name, and good luck. Thanks for reading this tutorial, and if you have comments or questions, please join in the forum discussion below!
Credit: All images in this post are from the public domain, and are available at: www.openclipart.org
Programming Challenge: Are You a Swift Ninja? Part 2 is a post from: Ray Wenderlich
The post Programming Challenge: Are You a Swift Ninja? Part 2 appeared first on Ray Wenderlich.