Skip to content

Instantly share code, notes, and snippets.

@loganwright
Last active August 29, 2015 14:02
Show Gist options
  • Save loganwright/819684b99c5545ed537b to your computer and use it in GitHub Desktop.
Save loganwright/819684b99c5545ed537b to your computer and use it in GitHub Desktop.
import Cocoa
/*
Up to this point, we've built fairly simple functions that take one or no arguments and return one or no values.
Let's first make a basic function that takes two arguments and returns their sum */
func sum(a: Int, b: Int) -> Int {
return a + b
}
var total = sum(14, 52)
/*
The syntax is almost identical to single argument syntax with the comma separating each argument. You'll notice that I didn't declare any type of external parameter with this function. That is because it is fairly obvious what the intent of the function is and we don't necessarily need them. If you prefer, you can be more explicit.
*/
/*
But what if we wanted more information about these numbers? Let's say we wanted a function that takes two numbers and returns their sum, but we also need to know which number is higher?
In Swift, this is possible! Let's look at the following example
*/
func sumAndCeiling(a: Int, b: Int) -> (Int, Int) {
var ceiling = a > b ? a : b
var sum = a + b
return (sum, ceiling)
}
/*
We can declare multiple returns by encapsulating them in parenthesis separated by a comma. The above function is basically saying:
"A function that takes two Int types and returns two Int types"
We can receive and interact with the Tuple by getting its indexes like so:
*/
var result = sumAndCeiling(4, 52)
result.0
result.1
/*
But what if we're doing something more complex with our tuple and using '0' and '1' might be confusing. If that's the case, you can declare external names to accompany the tuple by including them in your function declaration.
Let's use a slightly different example to show how we can be more explicit about what our tuple is returning. The syntax to declare these names is almost identical to that of argument declaration
*/
func sumAndFloor(a: Int, b: Int) -> (sum: Int, floor: Int) {
var floor = a < b ? a : b
var sum = a + b
return (sum, floor)
}
var tuple = sumAndFloor(13, 4)
tuple.sum
// is equal to
tuple.0
tuple.floor
// is equal to
tuple.1
/* FUNCTIONS AS TYPES
Like most things in Swift, at its core a function is just another type. This means that it can be passed as arguments, stored in variables and used in a variety of fun ways.
Every function has an inherent 'Type' made up of its parameters and its return following the syntax:
argumentType -> returnType
This means that if we have a basic function:
*/
func double(number: Int) -> Int {
return number * 2
}
/* It would be considred a function of type:
Int -> Int
Let's consider another function that follows the same type. A function that takes one Int arg and returns one Int
*/
func triple(number: Int) -> Int {
return number * 3
}
/*
Although these functions are very different, they are both considered to be of the same 'Type' and they could both be used anywhere a function of type: Int -> Int is allowed. Take the following example
*/
func modifyInt(#number: Int, #modifier:Int -> Int) -> Int {
return modifier(number)
}
/*
What we're really saying is that we'd like a function that takes an integer and a function of type Int -> Int as arguments and returns an Int.
We currently have two functions of type Int -> Int, and we can pass either to 'modifyInt' without problem
*/
modifyInt(number: 15, modifier: double)
modifyInt(number: 15, modifier: triple)
/*
Of course the above example is a bit contrived (like most examples)and it would probably be better just to call the functions directly, but this gives you a taste of some of the capabilities of the language. Functions can also return other functions in the same manner, but with an added twist that I'll talk about now.
*/
func buildIncrementor() -> () -> Int {
var count = 0
func incrementor() -> Int {
++count
return count
}
return incrementor
}
/*
This example (based off of the one in Apple's docs) shows how we can return a function from a function. Our declaration is saying that we need it to return a function that takes no arguments but returns an Int type.
That's not what's really special about the above argument though. What really makes this cool is the fact that our inner function 'incrementor' will encapsulate the 'count' variable within itself and we can use that to keep track of how many times something has run
*/
var incrementor = buildIncrementor()
incrementor() // 1
incrementor() // 2
incrementor() // 3
/* Variadic arguments
Another great new feature that is highly simplified in Swift is the ability to use variadic arguments. This is declared using the syntax:
SomeType...
Which in practice looks like this:
*/
func average(#numbers: Int...) -> Int {
var total = 0
for n in numbers {
total += n
}
return total / numbers.count
}
/*
This means that no matter how many arguments we supply, we'll still get the average quickly and easily because Swift automatically converts them to an interactive array. Calling the method will look like this:
*/
average(numbers: 13, 14, 235, 52, 5)
average(numbers: 123, 643, 6)
/*
At this point, I feel obligated to tell you that there is a small little issue with variadic functions that doesn't allow us to pass an array to them. This means that if we already have an array of numbers, we're going to run into problems. Luckily, there's an easy workaround that exploits yet another powerful language feature. Let's start by creating a similar function that combines all the strings in an array of strings*/
func joinStrings(#strings: String[]) -> String {
var returnStr = ""
for str in strings {
returnStr += str
}
return returnStr
}
/* But what happened to our variadic arguments :( Well, let's declare that one too! But instead of processing it ourselves, let's pass the array to the function we declared previously */
func joinStrings(#strings: String...) -> String {
return joinStrings(strings: strings)
}
/* Shouldn't that cause problems? Nope. Swift is smart enough to know which one we're looking for and this way we can interact with our function Variadically, or through an array */
var arrayOfStrings = ["I ", "am ", "an ", "array!"]
joinStrings(strings: arrayOfStrings)
joinStrings(strings: "I ", "am ", "variadic!")
/* Default parameter values & inout
Let's talk about two more awesome language features!
The ability to define default parameters. This allows some parameters to be omitted when calling the function if the default parameter is desired
The ability to pass references into a function to be modified -- It will make more sense in a minute.
Let's look at how this function might look
*/
func incrementNumber(inout #number: Int, increment: Int = 1) {
number += increment
}
/* There's a lot of new info in that declaration that we need to talk about. The first thing that looks different is the keyword 'inout'. All that means is that we're going to take a variable from outside the scope of the function and modify it within the scope of that function. We're going to take it 'in' and send it back 'out' ... inout. This is indicated in the function call via an ampersand. This is autocompleted for us, and you will see it shortly.
The second argument, `increment` declares a value in the declaration. By doing this, we can define a default value for that parameter. This means that when we call the function, we can modify that parameter to whatever we'd like, or stick with the default value and omit it completely. Default values should always be the last argument so that if they are omitted, there is less confusion.
There is no need to define an external parameter for the second argument because a "default argument implies keyword argument". Insert a '#' prefix to 'increment' and you'll see the compiler notification.
Let's look at what happens when we call this function
*/
// Declare our variable. Because we're using an inout function, we want to pass the value of this variable into the function to be modified. You can not use literals with inout parameters
var ourInt = 1
// Increments by default value: 1
incrementNumber(number: &ourInt)
ourInt
// Increments by 2
incrementNumber(number: &ourInt, increment:2)
ourInt
// Increments by 10
incrementNumber(number: &ourInt, increment:10)
ourInt
/*
This way we can have the option of the default increment, or we can explicitly modify it as needed.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment