Programming Challenge: Are You a Swift Ninja? Part 2

Do you consider yourself a Swift Ninja? Take our programming challenge! Beginners are also welcome to follow through and learn the craft. By Marin Todorov.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Contents

Hide contents

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 ?:.

3shurikens

[spoiler title="Hints"]

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.

2shurikens

[/spoiler]

[spoiler title="Tutorial"]

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:

  1. doWork() returns true, so there's a chance for "&&" to result in either false or true depending on the value of reportSuccess().
  2. reportSuccess() is evaluated and prints "success!".
  3. The result of doWork() && reportSuccess() is true.
  4. 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:

  1. doWork() returns false, so the result of "&&" is already false. reportSuccess() is never evaluated.
  2. Since the result so far is false the right side of the || has chance to turn the result into true so it gets evaluated.
  3. When evaluated reportError() prints out "error".

1shuriken

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.

[/spoiler]

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:

  1. list.swapElementAtIndex(index: Int)(withIndex: Int): Returns a copy of the original array with the elements at indexes index and withIndex exchanged.
  2. list.arrayWithElementAtIndexToFront(index: Int): Returns a copy of the original array with the element at index index exchanged with the first element .
  3. 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]

3shurikens

[spoiler title="Hints"]

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!

2shurikens
[/spoiler]

[spoiler title="Tutorial"]

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!

1shuriken
[/spoiler]

Contributors

Over 300 content creators. Join our team.