@Xiaojun-Jin
2015-03-27 09:31
字数 8241
阅读 1340
Swift
struct Pair<T: Equatable> {
let a: T!
let b: T!
init(a: T, b: T) {
self.a = a
self.b = b
}
func equal() -> Bool {
return a == b
}
}
let pair = Pair(a: 5, b: 10)
pair.a // 5
pair.b // 10
pair.equal() // false
let floatPair = Pair(a: 3.14159, b: 2.0)
floatPair.a // 3.14159
floatPair.b // 2.0
floatPair.equal() // false
This struct now can take parameters of any types (int, float or else) by using Generics
.
// automatically inferred
let array = [1, 2, 3, 4]
let dictionary = ["dog": 1, "elephant": 2]
// fully defining
let array: Array<Int> = [1, 2, 3, 4]
let dictionary: Dictionary<String, Int> = ["dog": 1, "elephant": 2]
// short form
let array: [Int] = [1, 2, 3, 4]
let dictionary: [String: Int] = ["dog": 1, "elephant": 2]
Instead of using the
isEqualToString:
method, in Swift you are able to directly use==
to compare strings.
switch person.name {
case "Matt Galloway":
println("Author of an interesting Swift article")
case "Ray Wenderlich":
println("Has a great website")
case "Tim Cook":
println("CEO of Apple Inc.")
default:
println("Someone else")
}
There are no breaks in sight. That's because cases in switches no longer fall through to the next one!
switch i {
case 0, 1, 2:
println("Small")
case 3...7:
println("Medium")
case 8..<10:
println("Large")
case _ where i % 2 == 0:
println("Even")
case _ where i % 2 == 1:
println("Odd")
default:
break
}
Here break
is added to declare that default will do noting. Note that switch
also has the ability to define a case as a calculation of the input.
Closure expression syntax has the following general form:
{ (parameters
) -> return type
in statements
}
{ (s1: String, s2: String) -> Bool in return s1 > s2 }
If the return types can be inferred (which is often the case), the return arrow (->), the parentheses, and keyword
return
can also be omitted.
{ parameters
in statements
}
{ s1, s2 in s1 > s2 }
// Shorthand Argument Names
{ $0 > $1 }
An array that contains [a, b, c] is really the same as another array that contains [a, b, c] and they are completely interchangeable. It doesn't matter whether you use the first array or the second, because they represent the exact same thing. That's why arrays are structs.
You would model a person as a class because two person objects are two different things. Just because two people have the same name and birthdate, doesn't mean they are the same person.
Perhaps you've seen a class declaration like this before:
class MyViewController: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate,
UINavigationControllerDelegate,
UIImagePickerControllerDelegate, UITextFieldDelegate
We won't judge you for having such a busy view controller, but we do suggest having each protocol conformance declared in its own extension:
class MyViewController {
// Standard view controller stuff here
}
extension MyViewController: UITableViewDelegate {
// Table view delegate methods here
}
extension MyViewController: UITextFieldDelegate {
// Text field delegate methods here
}
// etc.
This keeps things organized and helps keep related methods together. It also means you can add a protocol and related methods in one step in one place, rather than editing the class declaration and then adding methods somewhere to a potentially crowded list of methods.
let tap: UITapGestureRecognizer
// error: Property 'tap' not initialized at super.init call
let tap: UITapGestureRecognizer?
// defined as optional value to avoid force initializing in init function
// no return value, thus no return arrow (->) is included
func sayGoodbye(personName: String) {
println("Goodbye, \(personName)!")
}
Functions without a defined return type return a special value of type Void. This is simply an empty
tuple
, in effect a tuple with zero elements, which can be written as ().
func isEven(number: Int) -> Bool {
return number % 2 == 0
}
evens = Array(1...10).filter(isEven)
println(evens)
Functional filtering: Array(1...10)
creates an Array 1...10
creates a Range. The filter
statement creates and returns a new array that contains only the items for which the given function returns true.
evens = Array(1...10).filter { (number) in number % 2 == 0 }
println(evens)
Functions are just named closures. Compiler infers the type of the parameter number and return types of the closure from its usage context.
evens = Array(1...10).filter { $0 % 2 == 0 }
println(evens)
The above uses argument shorthand notation, implicit returns, type inference!
Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure's arguments by the names
$0
,$1
,$2
, and so on.
Generic Function:
func myFilter<T>(source: [T], predicate:(T) -> Bool) -> [T] {
var result = [T]()
for i in source {
if predicate(i) {
result.append(i)
}
}
return result
}
evens = myFilter(Array(1...10)) { $0 % 2 == 0 }
println(evens)
// or calling like this:
evens = myFilter(Array(1...10)) { number in number % 2 == 0 }
println(evens)
If generic function takes two function parameters:
func myFilter<T>(source: [T], predicate:(T) -> Bool, tFun:() -> ()) -> [T] {
var result = [T]()
for i in source {
if predicate(i) {
result.append(i)
}
}
tFun()
return result
}
func isEven(number: Int) -> Bool {
return number % 2 == 0
}
func tFun() -> () {
println("I am Rylan.")
}
let evens = myFilter(Array(1...10), isEven, tFun)
// alternatively
let evens = myFilter(Array(1...10), { $0 % 2 == 0 }, {println("I am Rylan.")})
If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a
Trailing Closure
instead.
// trailing closures
let evens1 = myFilter(Array(1...10),{ nuber in nuber % 2 == 0 }) { println("dd") }
// func reduce<U>(initial: U, combine: (U, T) -> U) -> U
// reduce(0) {...} trailing closures style
// 'typealias Element = T', so 'T' refers to array element?
evenSum = Array(1...10)
.filter { (number) in number % 2 == 0 }
.reduce(0) { (total, number) in total + number }
// concise version
evenSum = Array(1...10)
.filter { $0 % 2 == 0 }
.reduce(0) { $0 + $1 }
println(evenSum)
let numbers = Array(1...10)
.reduce("numbers: ") {(total, number) in total + "\(number) "} /*{ $0 + "\($1) " }*/
println(numbers)
Imperative method:
let words = ["Cat", "Chicken", "fish", "Dog",
"Mouse", "Guinea Pig", "monkey"]
// Using a typealias makes the code more readable
typealias Entry = (Character, [String])
func buildIndex(words: [String]) -> [Entry] {
var result = [Entry]()
var letters = [Character]()
for word in words {
let firstLetter = Character(word.substringToIndex(
advance(word.startIndex, 1)).uppercaseString)
if !contains(letters, firstLetter) {
letters.append(firstLetter)
}
}
for letter in letters {
var wordsForLetter = [String]()
for word in words {
let firstLetter = Character(word.substringToIndex(
advance(word.startIndex, 1)).uppercaseString)
if firstLetter == letter {
wordsForLetter.append(word)
}
}
result.append((letter, wordsForLetter))
}
return result
}
println(buildIndex(words))
Functional Way:
func buildIndex(words: [String]) -> [Entry] {
let letters = words.map {
(word) -> Character in
Character(word.substringToIndex(advance(word.startIndex, 1)
).uppercaseString)
}
// omit return type (->)
/*
let letters = words.map {
(word) in
Character(word.substringToIndex(advance(word.startIndex, 1)
).uppercaseString)
}
*/
// concise version
/*
let letters = words.map {
Character($0.substringToIndex(advance($0.startIndex, 1)
).uppercaseString)
}
*/
println(letters) // [C, C, F, D, M, G, M]
return [Entry]()
}
map
creates a new array with the results of calls to the supplied closure for each element in the supplied array. You use map
to perform transformations; in this case, map
transforms an array of type [String] into an array of type [Character].
func distinct<T: Equatable>(source: [T]) -> [T] {
var unique = [T]()
for item in source {
if !contains(unique, item) {
unique.append(item)
}
}
return unique
}
distinct
iterates over all the items in an array, building a new array that contains only the unique items.
Update buildIndex as follows:
func buildIndex(words: [String]) -> [Entry] {
let letters = words.map {
(word) -> Character in
Character(word.substringToIndex(advance(word.startIndex, 1)
).uppercaseString)
}
let distinctLetters = distinct(letters)
return distinctLetters.map {
(letter) -> Entry in
return (letter, words.filter {
(word) -> Bool in
Character(word.substringToIndex(advance(word.startIndex, 1)
).uppercaseString) == letter
})
}
}
Concise version (create func firstLetter to remove duplicate code):
func buildIndex(words: [String]) -> [Entry] {
func firstLetter(str: String) -> Character {
return Character(str.substringToIndex(
advance(str.startIndex, 1)).uppercaseString)
}
let letters = words.map {
(word) -> Character in
firstLetter(word)
}
let distinctLetters = distinct(letters)
return distinctLetters.map {
(letter) -> Entry in
return (letter, words.filter {
(word) -> Bool in
firstLetter(word) == letter
})
}
}
func filter(includeElement: (T) -> Bool) -> [T]
// Return anArray
containing the elementsx
ofself
for whichincludeElement(x)
istrue
Even more concise version:
func buildIndex(words: [String]) -> [Entry] {
func firstLetter(str: String) -> Character {
return Character(str.substringToIndex(
advance(str.startIndex, 1)).uppercaseString)
}
return distinct(words.map(firstLetter))
.map {
(letter) -> Entry in
return (letter, words.filter {
(word) -> Bool in
firstLetter(word) == letter
})
}
}