swift语法糖

介绍:

本文将记录一些特殊语法的使用方法,以及如何利用这些特殊语法给编程带来的方便,第一部分为语法记录,第二部分为编程技巧

一、语法记录

1.1 集合index相关

全新的集合用collection.index标识集合中元素位置。Array.indices 为数组中所有index的集合

let str2 = "12345678"
// collections.count
str2.count                                  // 8
// 获取集合中元素位置
str2[str2.indices.first!]                   // "1"
[82, 58, 76, 49, 88, 90].indices            // 0..<6
for a in str2.indices{
    print(a.encodedOffset)                  // 0,1,2,3,4,5,6,7
}

String分割一般用 ..< 例:string.startIndex..<string.endInde

var str = "Hello, playground"               
let range = str.index(str.startIndex, offsetBy: 0)..<str.index(str.startIndex, offsetBy: 5)
str[range]                                  // "Hello"

获取字符串内容字符串长度不确定,获取长度大于字符串长度时,返回整串,目标长度小于字符串长度返回截取的子串内容

var length = 10
var contentRange:Range<String.Index>
if let content = str.index(str.startIndex, offsetBy: length, limitedBy: str.endIndex) {
    contentRange = str.index(str.startIndex, offsetBy: 0)..<str.index(content, offsetBy: 0)
} else{
    contentRange = str.index(str.startIndex, offsetBy: 0)..<str.index(str.endIndex, offsetBy: 0)
}
str[contentRange]                        // "Hello, pla"
let a  = str[str.startIndex..<str.index(after: str.startIndex)] //"H"

1.2 @autoclosure(自动闭包)

自动闭包不接受任何参数,延迟求值,只有在被调用时才会返回被包装在其中的表达式的值。
在我们调用函数A获取函数结果作为参数传递给另一个函数B时,无论这个结果在函数B中是否使用,函数A都会被执行,如下第二次调用goodAfternoon(afternoon: false, who: getName())方法后并没有使用getName的值但是函数getName还是执行了

func getName() -> String{
    print(#function)
    return "DKJone"
}
func goodAfternoon(afternoon:Bool ,who:String){
    if afternoon {
        print("Good afternoon, \(who)")
    }
}
print("------goodAfternoon(afternoon: true, who: getName())-------")
goodAfternoon(afternoon: true, who: getName())
print("------goodAfternoon(afternoon: false, who: getName())-------")
goodAfternoon(afternoon: false, who: getName())
/*log:
    ------goodAfternoon(afternoon: true, who: getName())-------
    getName()
    Good afternoon, DKJone
    ------goodAfternoon(afternoon: false, who: getName())-------
    getName()
*/

当我们在第二个参数添加 @autoclosure 关键字后,第二个参数中的代码会在函数执行时自动生成一个闭包,只有闭包真正执行时第二个参数种类代码才会调用所以下面的goodMooning(morning: false, who: getName())并没有调用getName方法

//@autoclosure
func goodMooning(morning:Bool ,who:@autoclosure() -> String){
    if morning {
        print("Good morning, \(who())")
    }
}
print("------goodMooning(morning: true, who: getName())-------")
goodMooning(morning: true, who: getName())
print("------goodMooning(morning: false, who: getName())-------")
goodMooning(morning: false, who: getName())
/* log:
------goodMooning(morning: true, who: getName())-------
getName()
Good morning, DKJone
------goodMooning(morning: false, who: getName())-------
*/

1.3 sequence(first: next: )

根据next里的闭包来生成下一个元素,和reduce完全相反。特别的是这个函数返回的是一个 UnfoldSequence ,即里面的值是lazy的,只要在访问时才生成,这也可能是一个无限的队列。

for x in sequence(first: 0.1, next: { $0 * 2 }).prefix(while: { $0 < 4 }) {
    // 0.1, 0.2, 0.4, 0.8, ...
}

似乎特别适合用来寻祖,当next闭包返回的是nil时队列就终止了:

for view in sequence(first: someView, next: { $0.superview }) {
    // someView, someView.superview, someView.superview.superview, ...
}

二、编码的艺术

2.1 数组中的每个元素乘以2

        (1...7).map{$0 * 2}

2.2 数组中的元素求和/排序 在swift中+-*/><=都是一个函数/闭包

        (1...10).reduce(0, +)               // 55
        (1...10).sorted(by: > )             // [10,9,8,7,6, 5,4,3,2,1] 

2.3 验证在字符串中是否存在指定单词

        let words = ["Swift","iOS","cocoa","OSX","tvOS"]
        let tweet = "This is an example tweet larking about Swift"

        let valid = !words.filter({tweet.contains($0)}).isEmpty     // false
        //或者
        tweet.split(separator: " ")
            .lazy
            .map(String.init)
            .contains(where: Set(words).contains)

2.4 一行代码输出生日歌

        (1...4).forEach{print("Happy Birthday " + (($0 == 3) ? "dear Alice":"to You"))}
        /*log:
                Happy Birthday to You
                Happy Birthday to You
                Happy Birthday dear uraimo
                Happy Birthday to You
        */

2.5 在数组中查找最小(或最大)值

        //Find the minimum of an array of Ints
        [10,-22,753,55,137,-1,-279,1034,77].sorted().first
        [10,-22,753,55,137,-1,-279,1034,77].reduce(Int.max, min)
        [10,-22,753,55,137,-1,-279,1034,77].min()
        //Find the maximum of an array of Ints
        [10,-22,753,55,137,-1,-279,1034,77].sorted().last
        [10,-22,753,55,137,-1,-279,1034,77].reduce(Int.min, max)
        [10,-22,753,55,137,-1,-279,1034,77].max()

2.6.过滤数组中的数字 *partition 会根据条件把集合里的元素重新排序,符合条件的元素移动到最后,返回一个两个部分分界元素的索引.

        extension Sequence{
            //typealias Element = Self.Iterator.Element
            func partitionBy(fu: (Element)->Bool) -> ([Element],[Element]){
                var first = [Element]()
                var second = [Element]()
                for el in self {
                    if fu(el) {
                        first.append(el)
                    }else{
                        second.append(el)
                    }
                }
                return (first,second)
            }
        }
        let part = [82, 58, 76, 49, 88, 90].partitionBy{$0 < 60}
        part // ([58, 49], [82, 76, 88, 90])
        //简写一
        extension Sequence{
            func anotherPartitionBy(fu: (Self.Iterator.Element)->Bool)->([Self.Iterator.Element],[Self.Iterator.Element]){
                return (self.filter(fu),self.filter({!fu($0)}))
            }
        }
        let part2 = [82, 58, 76, 49, 88, 90].anotherPartitionBy{$0 < 60}
        part2 // ([58, 49], [82, 76, 88, 90])
        //实际应用版
        var part3 = [82, 58, 76, 49, 88, 90].reduce( ([],[]), {
            (a:([Int],[Int]),n:Int) -> ([Int],[Int]) in
            (n<60) ? (a.0+[n],a.1) : (a.0,a.1+[n])
        })
        part3 // ([58, 49], [82, 76, 88, 90])

2.7 通过解构元组交换

        var a=1,b=2
        (a, b) = (b, a)
        "a=\(a),b=\(b)"   // "a=2,b=1"

2.8 正则表达式

extension String{
    /// 移除小数点后结尾多余0和小数点
    public var removeEndZero:String{
        return replacingOccurrences(of: "[.]?[0]*$", with: "", options: [.regularExpression,.caseInsensitive])
    }
    /// 移除电话号码开头的 +086、086、86、+86
    public var remove086Phone:String{
        return replacingOccurrences(of: "^[+]?[0]?86", with: "", options: [.regularExpression,.caseInsensitive])
    }
}

2.9.限制输入数字字符

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    if text == "\n"{
        textView.resignFirstResponder()
        return false
    }
        return NSPredicate(format: "SELF MATCHES %@", argumentArray: ["[\\d]?[\\cx]?"]).evaluate(with: text)
}

2.10 读取一个文件

和其他语言不同,Swift 不能使用内建的函数读取文件,并把每一行存放到数组中。不过我们可以结合 splitmap 方法写一段简短的代码,这样就无需使用 for 循环:

let path = Bundle.main.path(forResource: "test", ofType: "txt")
let lines = try? String(contentsOfFile: path!).split{$0 == "\n"}.map(String.init)
if let lines=lines {
    lines[0] // aaaaa
    lines[1] // bbbbb
    lines[2] // ccccc
    lines[3] // ddddd
}