早在 [第 02 天] 基本變數類型我們就開始使用 Python 的內建函數(generic functions),像是我們使用 help()
函數查詢文件以及使用 type()
函數來觀察我們的變數類型為 str,int 或者 bool。對於 R 語言使用者而言,函數可是我們的最愛,因為本質上 R 語言是一個函數型編程語言(functional programming language)。
(Almost) everything is a function call.
By John Chambers
截至 2016-12-08 上午 8 時第 8 屆 iT 邦幫忙各組的鐵人分別是 50、8、16、12、6 與 62 人,我們將這個資訊存在一個 R 語言的 vector 或 Python 的 list,然後對這個資料結構使用一些內建函數。如果你對這兩個資料結構有點疑惑,我推薦你參考本系列文章的 [第 04 天] 資料結構 List,Tuple 與 Dictionary。
ironmen <- c(50, 8, 16, 12, 6, 62)
# 應用函數
max(ironmen) # 最多的鐵人數
min(ironmen) # 最少的鐵人數
length(ironmen) # 總共有幾組
sort(ironmen) # 遞增排序
sort(ironmen, decreasing = TRUE) # 遞減排序
ironmen = [50, 8, 16, 12, 6, 62]
# 應用函數
print(max(ironmen)) # 最多的鐵人數
print(min(ironmen)) # 最少的鐵人數
print(len(ironmen)) # 總共有幾組
print(sorted(ironmen)) # 遞增排序
print(sorted(ironmen, reverse = True)) # 遞減排序
不論是 R 語言或者 Python,我們都必須要求自己養成查詢函數文件的習慣,了解一個函數的輸入與它可以設定的參數,例如上面例子中,查詢文件之後就知道能在排序的函數中修改參數調整為遞增或遞減排序。
?sort
help(sort)
sorted?
help(sorted)
為了讓程式更加有組織性,更好管理與具備重複使用性,除了使用內建函數以外我們得要學習自訂函數。在自訂函數時候我們會使用迴圈與流程控制,如果你對這兩個概念有點疑惑,我推薦你參考本系列文章的 [第 07 天] 迴圈與流程控制。
R 語言自訂函數的架構:
function_name <- function(輸入, 參數 1, 參數 2, ...) {
# 函數做些什麼事
return(結果)
}
我們利用兩個練習來熟悉如何自訂函數。
第一個練習是輸入圓形的半徑長,依照參數的指定回傳面積或周長。
# 定義自訂函數
circle_calculate <- function(radius, area = TRUE) {
circle_area <- pi * radius^2
circle_circum <- 2 * pi * radius
if (area == TRUE) {
return(circle_area)
} else {
return(circle_circum)
}
}
# 呼叫自訂函數
my_radius <- 3
circle_calculate(my_radius) # 預設回傳面積
circle_calculate(my_radius, area = FALSE) # 指定參數回傳周長
第二個練習是寫程式的基本功交換排序法。
# 定義自訂函數
exchange_sort <- function(input_vector, decreasing = FALSE) {
input_vector_cloned <- input_vector # 複製一個輸入向量
# 遞增排序
if (decreasing == FALSE) {
for (i in 1:(length(input_vector) - 1)) {
for (j in (i + 1):length(input_vector)) {
# 如果前一個數字比後一個數字大則交換位置
if (input_vector_cloned[i] > input_vector_cloned[j]) {
temp <- input_vector_cloned[i]
input_vector_cloned[i] <- input_vector_cloned[j]
input_vector_cloned[j] <- temp
}
}
}
# 遞減排序
} else {
for (i in 1:(length(input_vector) - 1)) {
for (j in (i + 1):length(input_vector)) {
# 如果前一個數字比後一個數字小則交換位置
if (input_vector_cloned[i] < input_vector_cloned[j]) {
temp <- input_vector_cloned[i]
input_vector_cloned[i] <- input_vector_cloned[j]
input_vector_cloned[j] <- temp
}
}
}
}
return(input_vector_cloned)
}
# 呼叫自訂函數
my_vector <- round(runif(10) * 100) # 產生一組隨機數
my_vector # 看看未排序前
exchange_sort(my_vector) # 預設遞增排序
exchange_sort(my_vector, decreasing = TRUE) # 指定參數遞減排序
Python 自訂函數的架構:
def function_name(輸入, 參數 1, 參數 2, ...):
'''
Docstrings
'''
# 函數做些什麼事
return 結果
Python 使用者習慣加入 Docstrings 做自訂函數的說明,接著我們利用兩個練習來熟悉如何自訂函數。
第一個練習是輸入圓形的半徑長,依照參數的指定回傳面積或周長。
import math # 要使用 pi 得引入套件 math
# 定義自訂函數
def circle_calculate(radius, area = True):
'依據輸入的半徑與 area 參數計算圓形的面積或周長' # 單行的 docstring
circle_area = math.pi * radius**2
circle_circum = 2 * math.pi * radius
if area == True:
return circle_area
else:
return circle_circum
# 呼叫自訂函數
my_radius = 3
print(circle_calculate(my_radius)) # 預設回傳面積
print(circle_calculate(my_radius, area = False)) # 指定參數回傳周長
第二個練習是寫程式的基本功交換排序法。
import random # 呼叫函數時使用隨機整數
# 定義自訂函數
def exchange_sort(input_list, reverse = False):
''' # 多行的 docstrings
依據輸入的 list 與 reverse 參數排序 list 中的數字後回傳。
reverse 參數預設為 False 遞增排序,可以修改為 True 遞減排序。
'''
input_list_cloned = input_list
# 遞增排序
if reverse == False:
for i in range(0, len(input_list) - 1):
for j in range(i+1, len(input_list)):
# 如果前一個數字比後一個數字大則交換位置
if input_list_cloned[i] > input_list_cloned[j]:
temp = input_list_cloned[i]
input_list_cloned[i] = input_list_cloned[j]
input_list_cloned[j] = temp
# 遞減排序
else:
for i in range(0, len(input_list) - 1):
for j in range(i+1, len(input_list)):
# 如果前一個數字比後一個數字小則交換位置
if input_list_cloned[i] < input_list_cloned[j]:
temp = input_list_cloned[i]
input_list_cloned[i] = input_list_cloned[j]
input_list_cloned[j] = temp
return input_list_cloned
# 呼叫自訂函數
my_vector = random.sample(range(0, 100), 10) # 產生一組隨機數
print(my_vector) # 看看未排序前
print(exchange_sort(my_vector)) # 預設遞增排序
print(exchange_sort(my_vector, reverse = True)) # 指定參數遞減排序
使用 list 資料結構將回傳值包裝起來再依名稱呼叫。
# 定義自訂函數
ironmen_stats <- function(ironmen_vector) {
max_ironmen <- max(ironmen_vector)
min_ironmen <- min(ironmen_vector)
ttl_groups <- length(ironmen_vector)
ttl_ironmen <- sum(ironmen_vector)
stats_list <- list(max_ironmen = max_ironmen,
min_ironmen = min_ironmen,
ttl_groups = ttl_groups,
ttl_ironmen = ttl_ironmen
)
return(stats_list)
}
# 呼叫自訂函數
ironmen <- c(50, 8, 16, 12, 6, 62)
paste("最多:", ironmen_stats(ironmen)$max_ironmen, sep = '')
paste("最少:", ironmen_stats(ironmen)$min_ironmen, sep = '')
paste("總組數:", ironmen_stats(ironmen)$ttl_groups, sep = '')
paste("總鐵人數:", ironmen_stats(ironmen)$ttl_ironmen, sep = '')
在 return
後面將多個值用逗號 ,
隔開就會回傳一個 tuple。
# 定義自訂函數
def ironmen_stats(ironmen_list):
max_ironmen = max(ironmen_list)
min_ironmen = min(ironmen_list)
ttl_groups = len(ironmen_list)
ttl_ironmen = sum(ironmen_list)
return max_ironmen, min_ironmen, ttl_groups, ttl_ironmen
# 呼叫自訂函數
ironmen = [50, 8, 16, 12, 6, 62]
max_ironmen, min_ironmen, ttl_groups, ttl_ironmen = ironmen_stats(ironmen)
print("最多:", max_ironmen, "\n", "最少:", min_ironmen, "\n", "總組數:", ttl_groups, "\n", "總鐵人數:", ttl_ironmen)
我們常常懶得為一個簡單的函數取名字,R 語言與 Python 很貼心都支援匿名函數。
利用匿名函數 function(x) x * 30
把每組的鐵人數乘以 30 可以得到預期的各組文章總數。
ironmen <- c(50, 8, 16, 12, 6, 62)
ironmen_articles <- sapply(ironmen, function(x) x * 30)
ironmen_articles
Python 的匿名函數稱為 lambda 函數,利用 lambda x : x * 30
把每組的鐵人數乘以 30 可以得到預期的各組文章總數。
ironmen = [50, 8, 16, 12, 6, 62]
ironmen_articles = list(map(lambda x : x * 30, ironmen))
print(ironmen_articles)
第八天我們討論 Python 的函數,也與 R 語言相互對照,發現在使用內建函數時的語法大同小異;至於在自訂函數的結構上,我們需要記得 Python 有撰寫 Docstrings 的好習慣,使用縮排而非大括號 {}
區隔參數與函數主體,可以較簡便地回傳多個值,以及使用 lambda 關鍵字表示使用匿名函數。
同步刊登於 Github:https://github.com/yaojenkuo/learn_python_for_a_r_user