7. 函數(shù)
7.1. 函數(shù)的概念與作用
var_dump() 、sqrt()、floor() 、ceil() 在PHP中凡是帶()都可以理解為函數(shù)
函數(shù)分為系統(tǒng)函數(shù)和自定義函數(shù),系統(tǒng)函數(shù)是語言封裝好的,直接拿來使用即可,自定義函數(shù)需要事先定義,然后才能使用
函數(shù)不是數(shù)!
函數(shù)是一種代碼形式(語法形式),也即一種結(jié)構(gòu)。只要結(jié)構(gòu)****不執(zhí)行****,它是沒有任何意義的。
函數(shù)是將“若干行代碼”以一種語法形式包裝成的一個(gè)整體。
該整體可以做到“需要的時(shí)候就去執(zhí)行它”(就是執(zhí)行其中的若干行代碼)。
函數(shù)是解決在不同情形(不同代碼位置)下需要執(zhí)行相同代碼的有效方式——即所謂代碼重用。
入門:
輸出4行4列,5行5列,6行6列需要這么做:

改進(jìn):

7.2. 函數(shù)的定義與調(diào)用
定義語法形式:

說明:
1,函數(shù)名的命名規(guī)則,跟變量名一樣;
2,定義函數(shù)的目的就是為了以后能夠調(diào)用;
3,調(diào)用函數(shù),其實(shí)就是執(zhí)行函數(shù)中代碼;
4,形參,其實(shí)就是變量,是只能在該函數(shù)內(nèi)部使用的變量;
5,實(shí)參,其實(shí)就是數(shù)據(jù),是會(huì)傳入函數(shù)內(nèi)部的數(shù)據(jù)(是一一對(duì)應(yīng)地賦值給形參變量);
<u>案例演示:</u>
寫一個(gè)函數(shù),可以給定一個(gè)半徑,求對(duì)應(yīng)圓面積。

<u>課堂練習(xí):</u>
寫一個(gè)函數(shù),可以給定長寬高,求對(duì)應(yīng)一個(gè)長方體的表面積。

7.3. 函數(shù)參數(shù)(重點(diǎn))
7.3.1. 形參(形式參數(shù))
就是定義函數(shù)的時(shí)候,在函數(shù)名后的小括號(hào)中給出的變量名。
形參,只能在函數(shù)內(nèi)部使用——即該變量的使用范圍僅僅局限于當(dāng)前函數(shù)內(nèi)部。
形參的本質(zhì)是變量!

7.3.2. 實(shí)參(實(shí)際參數(shù))
就是調(diào)用函數(shù)的時(shí)候,在函數(shù)名后的小括號(hào)中給出的數(shù)據(jù)值。
實(shí)參的本質(zhì)是數(shù)據(jù)!

7.3.3. 函數(shù)參數(shù)的傳值方式
含義:
實(shí)參變量的值,以什么方式傳給形參。
說明:
其前提是:實(shí)參是一個(gè)變量的情況。
所以其實(shí)這里討論的是:兩個(gè)變量的傳值方式問題。
默認(rèn)情況下是值傳遞。
可以使用“&”符號(hào)設(shè)定為引用傳遞,形式如下:
function f1( $p1, &$p2, .... ){
。。。。。
}
此時(shí),在函數(shù)內(nèi)部,對(duì)該形參變量改變其值,則對(duì)應(yīng)的實(shí)參變量(在函數(shù)外部)的值也改變了。
<u>案例演示:</u>
//值傳遞
function test1($a){
$a=200; //形參$a只能在內(nèi)部使用
}
$a=100; // 實(shí)參
test1($a);
echo $a; // 100 //輸出的是實(shí)參
//引用傳遞 形參和實(shí)參共用一個(gè)內(nèi)存地址
function test(&$a){
//重新給形參賦值
$a=200;
}
$a=100;
test($a); //作用是給形參$a賦值
echo $a; //200
7.3.4. 形參的默認(rèn)值
形參可以設(shè)定默認(rèn)值。形式為:$形參名 = 某值。
設(shè)定默認(rèn)值的形參,只能放在沒有設(shè)定默認(rèn)值的形參的后面(右邊)。
設(shè)定了默認(rèn)值的形參對(duì)應(yīng)的實(shí)參可以不提供數(shù)據(jù),此時(shí)函數(shù)就會(huì)使用該默認(rèn)值當(dāng)做實(shí)參的值。
function f1($p1, $p2, $p3 = 3, $p4=true ){
//函數(shù)體語句塊
}

<u>案例演示:</u>
定義一個(gè)函數(shù),該函數(shù)可以計(jì)算給定半徑的球的體積,其中圓周率π默認(rèn)使用3.14,也可以根據(jù)不同精度的需要給定不同的圓周率。

7.4. 函數(shù)返回值(重點(diǎn))
含義:
一個(gè)函數(shù)在執(zhí)行結(jié)束時(shí),可以讓其返回一個(gè)數(shù)據(jù),這就是函數(shù)的返回值。
語法:
return 要返回的數(shù)據(jù);
說明:
1,一般情況下一個(gè)函數(shù)執(zhí)行結(jié)束都是需要返回一個(gè)數(shù)據(jù)值的。
2,函數(shù)也可以在執(zhí)行的中途返回?cái)?shù)據(jù),此時(shí),函數(shù)也是結(jié)束了的。
3,一個(gè)函數(shù)執(zhí)行得到的返回值,可以在任何需要數(shù)據(jù)的場(chǎng)合使用,跟使用一個(gè)變量數(shù)據(jù)一樣。
<u>演示:</u>
計(jì)算兩數(shù)的平方和與兩數(shù)的平方差相除的結(jié)果。

另外,return語句也可以不帶后面的數(shù)據(jù),此時(shí),就只是單純地結(jié)束函數(shù),并不返回?cái)?shù)據(jù)(也可以說返回null這個(gè)空數(shù)據(jù))。

7.5. 匿名函數(shù)
含義:
就是一個(gè)“定義時(shí)沒有名字”的函數(shù)。
此時(shí),就面臨一個(gè)問題,那就是:沒有名字,怎么調(diào)用呢?
實(shí)際上,此時(shí)它通過另一個(gè)方式來調(diào)用,如下所示:
形式一:
$f1 = function (形參...) { ....... } //這是定義匿名函數(shù)的形式。
$f1(實(shí)參); //這就是調(diào)用該調(diào)用??梢娖湔{(diào)用,跟可變函數(shù)的寫法非常類似。
形式二:
(function(形參){
//函數(shù)體
})(實(shí)參)
<u>案例:</u>
定義一個(gè)匿名函數(shù),該函數(shù)可以計(jì)算兩個(gè)數(shù)的和。


7.5.1. 靜態(tài)變量
什么是靜態(tài)變量?(僅限于在函數(shù)內(nèi)部)
在初始化時(shí)只會(huì)初始化一次,后面的值會(huì)一直跟著改變
在函數(shù)內(nèi)部使用static關(guān)鍵字修飾的變量,函數(shù)在多次調(diào)用的時(shí)候能夠共同使用該變量(跨函數(shù)共享變量)
語法:static $變量

7.6. 系統(tǒng)常用函數(shù)介紹
PHP語言,以函數(shù)極大豐富而聞名于世。
看手冊(cè),查手冊(cè),并作為一種學(xué)習(xí)的習(xí)慣:


7.6.1. 跟函數(shù)有關(guān)的函數(shù)
?function_exists(“函數(shù)名”):判斷一個(gè)函數(shù)是否已經(jīng)存在;
?func_get_arg( $n ): 在函數(shù)內(nèi)部可用,用于獲得第n個(gè)實(shí)參(n從0開始算起)
?func_get_args(): 在函數(shù)內(nèi)部可用,用于獲得所有實(shí)參,結(jié)果是一個(gè)數(shù)組
?func_num_args(): 在函數(shù)內(nèi)部可用,用于獲得實(shí)參的個(gè)數(shù)
上面3個(gè)函數(shù),可以讓我們?cè)谧远x的函數(shù)內(nèi)部,直接訪問(使用)實(shí)參數(shù)據(jù),而不依賴于形參變量。
這種特性,可以給我們定義某種“不確定有幾個(gè)數(shù)據(jù)需要計(jì)算的”場(chǎng)合。

定義一個(gè)函數(shù),該函數(shù)可以求出所給定的若干個(gè)數(shù)據(jù)中的奇數(shù)的和。
1,3,27,22,33,68,100

7.6.2. 字符串有關(guān)常用函數(shù)
·輸出與格式化:echo , print, printf, print_r, var_dump.
·字符串去除與填充:trim, ltrim, rtrim, str_pad
·字符串連接與分割:implode, join, explode, str_split
·字符串截?。簊ubstr, strchr, strrchr,
·字符串替換:str_replace, substr_replace
·字符串長度與位置: strlen, strpos, strrpos,
·字符轉(zhuǎn)換:strtolower, strtoupper, lcfirst, ucfirst, ucwords
·特殊字符處理:nl2br, addslashes, htmlspecialchars, htmlspecialchars_decode,
手冊(cè)》函數(shù)參考》文本處理》字符串》字符串函數(shù)
7.6.3. 常用數(shù)學(xué)函數(shù)(重點(diǎn))
max: 取得若干個(gè)數(shù)據(jù)中的最大值
min: 取得若干個(gè)數(shù)據(jù)中的最小值
round: 對(duì)某個(gè)數(shù)據(jù)進(jìn)行四舍五入(可以設(shè)定保留幾位小數(shù))
ceil: 對(duì)某個(gè)數(shù)“向上取整”:將一個(gè)數(shù)據(jù)往上找出其小的一個(gè)整數(shù)(含其本身)。
floor: 對(duì)某個(gè)數(shù)“向下取整”:將一個(gè)數(shù)據(jù)往下找出其大的一個(gè)整數(shù)(含其本身)
$n1 = floor(4.1); //4
$n2 = floor(4.9); //4
$n3 = floor(4); //4
$n4 = floor(-4.1); //-5
abs: 取得某個(gè)數(shù)據(jù)的絕對(duì)值
sqrt: 計(jì)算某個(gè)數(shù)的開方值
pow: 對(duì)某個(gè)數(shù)進(jìn)行“冪運(yùn)算”(就是獲得某個(gè)數(shù)的若干次方)
$n1 = pow(3, 2); //3的2次方,9
$n2 = pow(2, 3); //8
$n3 = pow(1.5, 2); //2.25
$n4 = pow(1.5, 2.5); //。。。。。1.5的2.5次方
$n5 = pow(9, 0.5); //3,就是開方,相當(dāng)于sqrt()
rand: 獲得某兩個(gè)數(shù)之間的隨機(jī)整數(shù)(含該兩個(gè)數(shù))
mt_rand: 獲得某兩個(gè)數(shù)之間的隨機(jī)整數(shù)(含該兩個(gè)數(shù)), 。
$n1 = mt_rand(0, 10); //隨機(jī)數(shù)在0-10之間(含)
手冊(cè)》函數(shù)參考》數(shù)學(xué)擴(kuò)展》Math》Math函數(shù)。
<u>演示案例:</u>
定義一個(gè)函數(shù),該函數(shù)可以返回所給定的任意兩個(gè)數(shù)字之間的隨機(jī)整數(shù)。

7.6.4. 常用時(shí)間函數(shù)
·time:獲得當(dāng)前時(shí)間(精確到秒),結(jié)果其實(shí)一個(gè)“整數(shù)”而已,代表從1970年1月1日0:0:0秒到當(dāng)前時(shí)刻的秒數(shù)。
·microtime:獲得當(dāng)前時(shí)間(可以精確到微秒)
·mktime:創(chuàng)建一個(gè)時(shí)間數(shù)據(jù),參數(shù)為:時(shí)、分、秒,月、日、年
·date:將一個(gè)時(shí)間轉(zhuǎn)換為某種字符串形式

·idate:取得一個(gè)時(shí)間的某個(gè)單項(xiàng)數(shù)據(jù)值,比如idate(“Y”)取得年份數(shù)
·strtotime:將一個(gè)字符串“轉(zhuǎn)換”為時(shí)間值;
·date_default_timezone_set:在代碼中設(shè)置“時(shí)區(qū)”
·date_default_timezone_get:在代碼中獲取“時(shí)區(qū)”


<u>案例:</u>
課堂電腦性能大比拼:
計(jì)算從1加到1000萬,看花了多少時(shí)間?
做法:先獲得一個(gè)時(shí)間,然后計(jì)算,然后再獲得一個(gè)時(shí)間,后一個(gè)時(shí)間,減前一個(gè)時(shí)間,就是耗時(shí)。
補(bǔ)充:
調(diào)試技巧:
1、第一步,找出出現(xiàn)錯(cuò)誤的文件,然后找到對(duì)應(yīng)的行號(hào)。

2、分析錯(cuò)誤原因,閱讀錯(cuò)誤信息(可以百度,也可以根據(jù)關(guān)鍵單詞,進(jìn)行猜)

1、如果沒有致命錯(cuò)誤,但是結(jié)果不是我們預(yù)期,或者我們想提前了解下這個(gè)步驟可能產(chǎn)生的結(jié)果,怎么辦?只能進(jìn)行單步調(diào)試。
控制:
exit
die
return
exit()
die()
8. 函數(shù)相關(guān)
8.1. 變量的作用域問題(重點(diǎn))
簡(jiǎn)單來說,有3種作用域:局部作用域,全局作用域,超全局作用域;
相對(duì)應(yīng)的,有3種變量: 局部變量, 全局變量, 超全局變量;

8.1.1. 局部作用域與局部變量:
就是函數(shù)內(nèi)部范圍的作用域,其中定義的變量就是局部變量(包括形參也是局部變量)。
局部變量只能在其所在的局部作用域中使用(訪問)。
局部變量在函數(shù)調(diào)用結(jié)束時(shí),會(huì)被自動(dòng)銷毀(可以理解為函數(shù)執(zhí)行結(jié)束,該執(zhí)行空間也被銷毀了)。

8.1.2. 全局作用域與全局變量:
就是函數(shù)外部范圍的作用域,其中定義的變量就是全局變量。
全局變量只能在其所在的全局作用域中可以直接使用(訪問)。

8.1.3. 超全局作用域與超全局變量:
包括局部作用域和全局作用域的的整個(gè)作用域范圍。
超全局變量可以在所有范圍中使用(訪問)。
實(shí)際上,只有有限的10來個(gè)系統(tǒng)預(yù)定義變量是超全局變量,包括:$_GET, $_POST, $_REQUEST等。
所以,系統(tǒng)預(yù)定義變量,也被統(tǒng)稱為超全局變量。
PHP中的不同作用域的圖示:


一個(gè)特別的超全局變量:
$GLOBALS它也是一個(gè)數(shù)組,其中存儲(chǔ)了我們自己定義的所有全局變量。
每個(gè)全局變量的變量名,就是$GLOBALS數(shù)組的一個(gè)單元。
比如:
在全局作用域中定義如下變量:
$v1 = 1; //這一行執(zhí)行,就有了一個(gè)這個(gè):$GLOBALS[‘v1’], 其值為1
$v2 = ‘a(chǎn)bc’; //這一行執(zhí)行,就有了一個(gè)這個(gè):$GLOBALS[‘v2’], 其值為’abc’
$v3 = true; //這一行執(zhí)行,就有了一個(gè)這個(gè):$GLOBALS[‘v3’], 其值為true
一個(gè)特別的的關(guān)鍵字:global
作用:
用于在局部作用域中,修飾一個(gè)跟全局變量同名的局部變量。
此時(shí)該局部變量也可以使用全局變量的值了——實(shí)際上他們其實(shí)是類似變量引用關(guān)系。
正常來說: 全局變量只能在函數(shù)外部使用,局部變量只能在函數(shù)內(nèi)部使用。
全局變量在局部使用:$GLOBALS

局部變量在全局使用:global

8.2. 遞歸函數(shù)(重點(diǎn)/難點(diǎn))
基本含義:
就是一個(gè)函數(shù)內(nèi)部再調(diào)用該函數(shù)本身的一種情形,這是語法形式上的。
具體場(chǎng)景是:
如果要解決的“最終問題”,可以根據(jù)比該問題“小一級(jí)”的問題的答案而得到解決,
并且,該“小一級(jí)”的問題,還可以根據(jù)比其“更小一級(jí)”的問題的答案而得到解決,
以此類推,直到“最小一級(jí)”的問題。如果最小一級(jí)問題已知,則最終的問題也就解決了。
危險(xiǎn):
如果函數(shù)在執(zhí)行的過程中沒有一個(gè)“不再調(diào)用”的終結(jié)機(jī)制,那么就會(huì)出現(xiàn)“停不下來”的現(xiàn)象。
遞歸:
遞歸入口(當(dāng)要執(zhí)行的后面的方法跟前面方法一致時(shí),就可以使用前面的方法)
遞歸點(diǎn)(當(dāng)執(zhí)行完了整個(gè)流程時(shí),需要跳出循環(huán))
原理:

<u>遞歸調(diào)用過程的代碼演示:</u>
分析一下代碼的輸出結(jié)果:

<u>案例1:</u>
計(jì)算5的階層;
分析:
數(shù)學(xué)上階乘可以這樣來描述:一個(gè)數(shù)n的階乘,是n-1的階乘,乘以n的結(jié)果!
假設(shè),我們有一個(gè)函數(shù) jieceng(),它可以計(jì)算任意正整數(shù)n的階乘,類似這樣:
$n = 5; //或等于10, 13,等等都無所謂。
$result = jiecheng($n);

<u>案例2:</u>
計(jì)算斐波那契數(shù)列第10項(xiàng)的值:1, 1, 2, 3, 5, 8, 13, 21, 34,55,89......
假設(shè)有個(gè)函數(shù),可以計(jì)算斐波那契數(shù)列的第n項(xiàng):

作業(yè): 猴子吃桃,第一天吃總數(shù)的一半多1個(gè),。。。以后的每一天都這樣吃。第10天吃完的時(shí)候還有1個(gè),問一共有多少個(gè)桃子。