一、默认参数值和可变参数值,后面会提到内部

作者: 编程  发布:2019-11-22

这一节将会提到Swift中其他的关于函数的应用,这些内容包括有:

函数

今天学习的内容是有关函数、闭包的知识.
1.函数

默认参数值和可变参数值、常量参数、返回函数类型和函数嵌套:

函数基础

在Swift中,函数前面要用func关键字声明,参数写在括号里,并用向右的箭头指向返回值的类型。

func sayHello(name: String?) -> String {
    return "Hello " + (name ?? "Guest")
}
var nickName : String? = nil
sayHello(nickName)
nickName = "MC"
sayHello(nickName)

//无参数,无返回值
func printHello1() {
    print ("Hello")
}
func printHello2() -> (){
    print ("Hello")
}
func printHello3() -> Void{
    print ("Hello")
}
  • 1.1 函数名、函数参数命名
    苹果编写函数的风格,就是函数的命名更加类似于自然语句,有着非常强的可读性.正常我们的函数是是这样定义的:

一、默认参数值和可变参数值

使用元组返回多个值

时刻要考虑到函数的参数或返回类型为可选型的情况。

func findMaxAndMin (numbers:[Int]) -> (max:Int , min:Int)? {
    guard numbers.count > 0 else {
        return nil
    }
    var minValue = numbers[0]
    var maxValue = numbers[0]
    for number in numbers {
        minValue = minValue < number ? minValue : number
        maxValue = maxValue > number ? maxValue : number
    }
    //return返回的元组里面的各个分量可以不用指明变量名,如果指明必须与函数返回值声明的时候一样。
    return (maxValue , minValue)
}
var scores: [Int]? = [202,123,4324,543,123]
scores = scores ?? []
if let result = findMaxAndMin(scores!) {
    print("The max score is (result.max)")
    print("The min score is (result.min)")
}

对默认参数值是指在函数的声明中就已经定义了参数的初值,比如:

内部参数名与外部参数名

Swift的函数默认会不显示第一个参数的参数名,但后面的参数会显示出他们的参数名,但这不是绝对的,后面会提到内部参数名和外部参数名的概念。

func sayHelloTo(name: String , greeting: String) -> String {
    return "(greeting),(name)!"
}
//sayHelloTo("Playground", "Hello")     //会报错
sayHelloTo("Playground", greeting: "Hello")

Swift中函数的每一个参数都可以有一个内部参数名和一个外部参数名。外部函数名用于调用函数时显示。没有外部参数名时,原来的参数名默认为内外参数名,当然我们也可以用下划线"_"来让函数被调用时不显示参数名。

func sayHelloTo(name: String , withGreetingWord greeting: String) -> String {
    return "(greeting),(name)!"
}
sayHelloTo("Playground", withGreetingWord: "Hello")


func mutiply (num1 : Int , _ num2 : Int) -> Int {
    return num1 * num2
}
mutiply(2, 3)

内外参数名的使用场景:往往是方面调用者来调用函数,使语义更加明确,苹果官方就是这么做的:

var arr = [1,2,3,4,5]
arr.insert(5, atIndex: 3)
max(2, 3)

补充:下划线"_"目前我们在for-in循环,switch语句和元组中都用到过,这里在函数参数名的时候也用到了。

func sayHelloTo(name: String, greeting: String) -> String {
    return "say (greeting) to (name)"
}

sayHelloTo("jf", greeting: "hello")
func sayHelloTo(name:String,greeting:String = “Hi”, punctuation:String = “!”){
}
在调用的时候可以是:
sayHelloTo(name:“PlayGround”, punctuation :“!!!”, greeting :“Hi”)

默认参数值与可变参数

当一个函数有默认参数值时,调用函数的时候会多一种调用方式,即不显示默认参数名称,相应的参数值为默认值。默认参数之间的顺序是可以改变的。默认参数可以放在参数列表的最后,也可以不放在最后,但这时候要注意,当默认参数不在参数列表最后的时候,参数之间的调用顺序是不许改变的。

func sayHelloTo (name: String , withGreetingWord greeting: String = "Hello" , punctuation: String = "!") -> String{
    return "(greeting), (name)(punctuation)"
}
sayHelloTo("MC")
sayHelloTo("MC", withGreetingWord: "Hi")
sayHelloTo("MC", withGreetingWord: "Hi", punctuation: "!!!")
sayHelloTo("MC", punctuation: "!!!")
sayHelloTo("MC", punctuation: "!!!" , withGreetingWord: "Hi")

对于一个函数来说,最多只能有一个变长参数类型

func mean( numbers: Double ...) -> Double {
    var sum: Double = 0
    //将变长参数当做一个数组看待
    for number in numbers {
        sum += number
    }
    return sum / Double(numbers.count)
}
mean(2)
mean (2,3,4,5)

参数:greeting 既是函数外部参数名,也是函数内部参数名
要是我们想要区分开,使得调用时,语义看起来更明确,又不会影响内部函数的参数调用,这时我们应该这样写:

因为对有默认参数的函数来说,有默认参数值的参数是可以调换顺序的,但是不建议这样使用。print函数就是有分割符和结束符初始值的函数。

常量参数,变量参数与inout参数

在Swift2.2中,函数参数声明为var将会产生一个警告。在Swift3.0中,该语法将被取消。也就是说,对于函数的参数,我们将或者指定为inout类型,或者不指定,默认为let参数。如果需要一个变量参数,则需要在函数体内标注:var num = num。

func toBinary(num: Int) -> String {
    var num = num
    var res = ""
    repeat {
        res = String(num%2) + res;
        num /= 2
    }while num != 0
    return res
}
var x = 100
toBinary(x)     //1100100
x               //x的值并没有被改变

若要在函数内修改完一个值之后,在函数外这个值依旧是被改变的,则要在函数参数类型上表明inout类型,并且在传参数的时候,参数前面要加一个取址符号"&"。

func swapTwoInts(inout a:Int , inout _ b:Int) {
    (a , b) = (b , a)
}
var x:Int = 1
var y:Int = 2
swapTwoInts(&x, &y)
x           //2
y           //1

注意:Swift里的数组,字典,集合等都是按值传递的。

func initArray(inout arr:[Int] , by value:Int) {
    for i in 0..<arr.count {
        arr[i] = value
    }
}
var arr = [1,2,3,4,5]
initArray(&arr, by: 0)
arr             //[0,0,0,0,0]
func sayHelloTo(name: String, withGreeting greeting: String) -> String {
    return "say (greeting) to (name)"
}

sayHelloTo("jf", withGreeting: "hello")

对变长的参数类型的函数来说,

使用函数类型

在Swift中函数也可以当做一个变量,也有相应的类型,即函数型变量。

func add(a:Int , _ b:Int) -> Int {
    return a + b
}
let anotherAdd = add           //(Int, Int) -> Int类型
anotherAdd(3,4)
//无参数无返回值类型的函数类型可以有下面四种方式表示:
() -> ()
() -> Void
Void -> ()
Void -> Void

下面举一个在实际编程中会用到函数类型的例子:

var arr:[Int] = []
for _ in 0..<100 {
    arr.append(random()%1000)
}
arr.sort()          //默认从小到大排序
arr.sort(<#T##isOrderedBefore: (Int, Int) -> Bool##(Int, Int) -> Bool#>)         
//里面可以传一个(Int, Int) -> Bool类型的函数来告诉数组怎么排序.

//从大到小
func biggerNumberFirst(a:Int , _ b:Int) -> Bool {
    return a > b
}
arr.sort(biggerNumberFirst)

//按照字符串的字典序进行排序
func cmpByNumberString(a:Int , _ b:Int) -> Bool {
    return String(a) < String(b)
}
arr.sort(cmpByNumberString)

//按照距离500最近的数排序
func near500(a:Int , _ b:Int) -> Bool {
    return abs(a - 500) < abs(b - 500)
}
arr.sort(near500)

还有一种情况,函数名已经十分明确了,这时候我们不需要函数外部参数名,该如何定义呢,请看:

func mean(numbers : Double ...)-> Double {
    for number in numbers {
        sum+=number
    }
    return sum/Double(numbers.count)
}

调用可以是:
mean(2)
也可以是
mean(2,3,4,5,6)

函数式编程初步

我们以改分系统为例来介绍这个章节的内容:

//常用写法
func changeScores1(inout scores:[Int]){
    for (index , score) in scores.enumerate() {
        scores[index] = Int(sqrt(Double(score)) * 10)
    }
}

func changeScores2(inout scores:[Int]){
    for (index , score) in scores.enumerate() {
        scores[index] = Int(Double(score) / 150.0 * 100.0)
    }
}

var scores1 = [36,61,78,89,99]
changeScores1(&scores1)

var scores2 = [88,101,124,137,150]
changeScores2(&scores2)

//改进写法
func changeScores(inout scores:[Int] , by changeScore: (Int) -> Int){
    for (index , score) in scores.enumerate() {
        scores[index] = changeScore(score)
    }
}
func changeScores1(score:Int) -> Int{
    return Int(sqrt(Double(score)) * 10)
}
func changeScores2(score:Int) -> Int{
    return Int(Double(score) / 150.0 * 100.0)
}

数组内容的扩展:

//map
changeScores(&scores1, by: changeScores1)
scores1.map(changeScores1)
//返回值可以不仅仅是整型
func isPassOrFail(score: Int) -> String {
    return score < 60 ? "Fail" : "Pass"
}
scores1.map(isPassOrFail)

//filter
func fail(score: Int) -> Bool {
    return score < 60
}
scores1.filter(fail)

//reduce
var arr = [1,2,3,4,5]
func add(num1:Int , _ num2: Int) -> Int {
    return num1 + num2
}
arr.reduce(0, combine: add)
arr.reduce(0, combine: +)

func concatenate(str:String , num: Int) -> String {
    return str + String(num) + " "
}
arr.reduce("", combine: concatenate)
func mutiply(num1: Int, _ num2: Int) -> Int {
    return num1 * num2
}

mutiply(1, 3)

但是一个函数只可以有一个变长的参数。

返回函数类型与函数嵌套

函数可以作为返回值,并且函数内部可以嵌套函数:

//邮费选择
func tier1MailFeeByWeight(weight: Int) -> Int {
    return 1 * weight
}

func tier2MailFeeByWeight(weight: Int) -> Int {
    return 3 * weight
}

//总价钱
func feeByUnitPrice(price: Int , weight: Int) -> Int {
    func chooseMailFeeCalculationByWeightt(weight: Int) -> (Int) -> Int {
        return weight <= 10 ? tier1MailFeeByWeight : tier2MailFeeByWeight
    }
    let mailFeeByWeight = chooseMailFeeCalculationByWeightt(weight)
    return mailFeeByWeight(weight) + price * weight
}
  • 1.2 默认参数值和可变参数值
    有默认参数值

 

闭包

二、常量参数

闭包的基础语法

Swift里面的闭包,跟我们OC里面的block基本是一样的。闭包本质上就是函数。

var arr:[Int] = []
for _ in 0..<100{
    arr.append(random()%1000)
}
func biggerNumberFirst(a:Int , _ b:Int) -> Bool {
    return a > b
}
arr.sort(biggerNumberFirst)

//使用闭包
arr.sort({ (a:Int,b:Int) -> Bool in
    return a > b
})
// 建议有默认参数值的参数放在最后面
func sayHelloTo(name: String, withGreeting greeting: String = "Hello") -> String {
    return "say (greeting) to (name)"
}

sayHelloTo("dd")
sayHelloTo("jf", withGreeting: "hello")

与Java和C/C++不同,Swift不可以在函数中改变参数的值,因为传入参数的值的类型声明是let,好的函数会尽量避免需要改变函数参数的值。

闭包语法的简化

对于一句话的闭包,我们可以有如下的化简:

arr.sort({ (a:Int,b:Int) -> Bool in
    return a > b
})

arr.sort({ (a:Int,b:Int) -> Bool in return a > b})

arr.sort({ a , b in return a > b})

arr.sort({ a , b in a > b})

arr.sort({ $0 > $1})

arr.sort(>)

变长的参数类型,对于一个函数最多有一个变长参数类型

但是如果要改变参数的值,需要这样来声明:

结尾闭包

当闭包为一个函数要传入的最后一个参数时,闭包可以写在括号的外面。

arr.sort({ a , b in return a > b})

arr.sort(){ a, b in
    return a > b
}

arr.sort{ a, b in
    return a > b
}
func sayHelloTo(names: String ..., withGreeting greeting: String) {
    for name in names {
        print("(name), (greeting)")
    }
}

sayHelloTo("jf", "DC", withGreeting: "Hello")
func toBinary(var num:Int)-> String {
    return num/2
}

内容捕获

前面我们所用的闭包,都是用来当做函数来使用的,但在一些情况下,闭包是有自己独特的优势的。

//sort里面传入的函数类型是确定的,我们无法传递第三个参数,这时可以利用闭包的内容捕获功能来解决这个问题。
arr.sort(<#T##isOrderedBefore: (Int, Int) -> Bool##(Int, Int) -> Bool#>)

var arr:[Int] = []
for _ in 0..<100{
    arr.append(random()%1000)
}

arr.sort{ a, b in
    abs(a - 500) < abs(b - 500)
}

var num = 300

arr.sort{ a, b in
    abs(a - num) < abs(b - num)
}
  • 1.3 常量参数、变量参数、inout参数
    一般,我们传入函数的参数是默认为常量参数,如果需要一个变量参数(事实上,由于面向函数的原则,不建议声明架构一个函数包含有变量参数。),则需要在函数体内标注:var num = num.

将类型声明为var,

闭包和函数是引用类型

之前我们说过,Swift中数组,字典和集合都是值类型,现在我们将接触到Swift中的第一个引用类型。

func runningMetersWithMetersPerDay(metersPerDay: Int) -> () -> Int {
    var totalMeters = 0
    return {
        totalMeters += metersPerDay
        return totalMeters
    }
}

var planA = runningMetersWithMetersPerDay(2000)
planA()             //2000
planA()             //4000
planA()             //6000
var planB = runningMetersWithMetersPerDay(5000)
planB()             //5000
planB()             //10000
planB()             //15000
var anotherPlan = planB
anotherPlan()       //20000
//证明是引用类型
planB()             //25000

但是这样的改变只是在函数的内部改变,但是在函数的外面是不改变的,

// 十进制数转二进制
func toBinary(num: Int) -> String {
    var num = num

    var res = ""

    repeat {
        res = String(num % 2) + res
        num /= 2
    } while num != 0

    return res
}

toBinary(12)

这是因为这种方式是按值出传入的,不是引用传入,只是在函数的内部进行了copy

inout参数:可以改变传入的值,做到按引用传入.

如果需要痛殴函数改变外部参数的值,需要添加inout

// 交换两个整数
func swapTwoInts(inout a: Int, inout _ b: Int) {
    let t: Int = a
    a = b
    b = t
}

var x = 1
var y = 2

swapTwoInts(&x, &y) // 传入参数前加上&
x // 2
y // 1
func swapInt(inout a :Int, inout b : Int){
    (a,b) = (b,a)
}
var x : Int = 1
var y : Int = 2
swapInt(&x,&b)
  • 1.4 函数型类型
    函数本身是可以被当做变量的

 

三、返回函数类型

// 函数本身是可以被当做变量的
var array1 = ["F", "P", "C", "D", "A", "S"]
func stringSortDesc(a: String, _ b: String) -> Bool {
    return a > b
}

let array2 = array1.sort(stringSortDesc)
array2


func sayHelloTo(names: String ..., withGreeting greeting: String) {
    for name in names {
        print("(name), (greeting)")
    }
}

func toBinary(num: Int) -> String {
    var num = num

    var res = ""

    repeat {
        res = String(num % 2) + res
        num /= 2
    } while num != 0

    return res
}

// 函数变量
let tobinary: (Int)->String = toBinary
let sayhello: (String ...,String)->() = sayHelloTo

这个也很简单,返回的类型是函数就好了:

下面了解初步的函数式编程例子

func tire1 (weight : Int)-> Int {
}
func tire2 (weight : Int ) ->Int {
}
func choose (weight : Int) -> (Int) -> Int {
    return weight>10 ? tire1(weight) : tire2(weight)
}
var score = [99, 20, 60, 80, 30, 21]

// map
func isPassOrFail(score: Int) -> String {
    return score < 60 ? "Fail" :"Pass"
}

score.map(isPassOrFail)

// filter
func arrayLower60(score: Int) -> Bool {
    return score < 60
}

score.filter(arrayLower60)

// reduce
func concatenate(str: String, score: Int) -> String {
    return str + "(score)" + " "
}

score.reduce("", combine: concatenate)

// 从大到小-转成字符串
score.sortInPlace { (score1, score2) -> Bool in
    return score1 > score2
}
score.reduce("", combine: concatenate)

 

  • 1.5 返回值为函数类型,函数嵌套
    例如:

四、函数的嵌套

func feeByPrice(price : Int,weight:Int)->Int {

  func choose (weight : Int) -> (Int) -> Int {
      return weight>10 ? tire1(weight) : tire2(weight)
  }
return ...
}
对于买家购买的商品,所付的钱 = 邮费 + 单价 * 体重/钱
而邮费的计算是根据体重,有不同的标准
标准1:小于20,邮费 =  体重
标准2:大于20,邮费 =  体重 * 2

func postage1(weight: Int) -> Int {
    return weight
}

func postage2(weight: Int) -> Int {
    return weight * 2
}

func choosePostage(weight: Int) -> (Int) -> Int {
    return weight < 20 ?postage1 :postage2
}

func calculateGoodPrice(weight: Int, price: Int) -> Int {
    let postage = choosePostage(weight)

    return postage(weight) + weight * price
}

calculateGoodPrice(30, price: 10)

 

函数嵌套

func postage1(weight: Int) -> Int {
    return weight
}

func postage2(weight: Int) -> Int {
    return weight * 2
}

func calculateGoodPrice(weight: Int, price: Int) -> Int {
    func choosePostage(weight: Int) -> (Int) -> Int {
        return weight < 20 ?postage1 :postage2
    }

    let postage = choosePostage(weight)

    return postage(weight) + weight * price
}

calculateGoodPrice(30, price: 10)

2.闭包
闭包和函数本质上是一致的,都属于引用类型.
学过OC的朋友,就可以把闭包当做block来理解.
闭包的基本语法

let hello = {(name: String, greeting: String) -> String in
    // 函数体
    return "(name): (greeting)"
}

一对大括号,参数列表,返回类型,关键字in,加上函数体

结尾闭包:若函数最后一个参数是闭包,则闭包可以写在小括号()后面,例如

let showView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))

showView.addSubview(rectangle)
rectangle.center = showView.center
rectangle.backgroundColor = UIColor.redColor()

UIView.animateWithDuration(2.0) { 
    rectangle.backgroundColor = UIColor.blueColor()
    rectangle.frame = showView.frame
}

内容捕获:在闭包内,可以获取到闭包外的变量(但是得注意内存问题,还有待研究)

为大家介绍用typealias,为闭包重新定义名字

typealias SayHello = (name: String, greeting: String) -> Void

let sayHello: SayHello? = {name, greeting in
    print("(name): (greeting)")
}

sayHello!(name: "jj", greeting: "Hi")


let hello = {(name: String, greeting: String) -> String in
    // 函数体
    return "(name): (greeting)"
}

swift中没有main函数
@UIApplicationMain:表示程序入口

大家赶紧去试试吧!

刘雨波Demo地址:
Play-with-Swift-2

本文由9159.com发布于编程,转载请注明出处:一、默认参数值和可变参数值,后面会提到内部

关键词: