函数定义
函数定义语法
TianYuan 使用 Matlab 风格的函数定义语法:
function [output1, output2, ...] = functionName(input1, input2, ...)
# 函数体
# ...
# 计算输出值
end
语法要点
- 函数定义以
function关键字开始 - 输出参数用方括号
[]括起来(单个输出可省略方括号) - 函数名遵循变量命名规则
- 输入参数用圆括号
()括起来 - 函数体以
end关键字结束
无输入输出的函数
最简单的函数形式:
function sayHello()
disp("Hello, TianYuan!")
end
# 调用函数
sayHello()
单个返回值
返回单个值的函数:
基本示例
function result = square(x)
result = x * x
end
# 使用函数
y = square(5) # y = 25
z = square(3.5) # z = 12.25
更多示例
# 计算圆的面积
function area = circleArea(radius)
area = pi * radius^2
end
# 华氏度转摄氏度
function celsius = f2c(fahrenheit)
celsius = (fahrenheit - 32) * 5/9
end
# 计算阶乘
function result = factorial(n)
result = 1
for i = 1:n
result = result * i
end
end
# 使用这些函数
A = circleArea(5) # 78.54
temp = f2c(98.6) # 37
fact = factorial(5) # 120
简化写法
对于单个返回值,可以省略方括号:
# 这两种写法等价
function result = add(a, b)
result = a + b
end
function [result] = add(a, b)
result = a + b
end
多个返回值
函数可以返回多个值:
基本语法
function [output1, output2] = functionName(input)
# 计算 output1 和 output2
output1 = ...
output2 = ...
end
# 调用时接收多个返回值
[result1, result2] = functionName(input)
示例:矩形属性
function [area, perimeter] = rectangle(width, height)
area = width * height
perimeter = 2 * (width + height)
end
# 使用函数
[A, P] = rectangle(5, 3)
disp("面积:" + num2str(A)) # 15
disp("周长:" + num2str(P)) # 16
示例:统计信息
function [minVal, maxVal, meanVal] = stats(data)
minVal = min(data)
maxVal = max(data)
meanVal = mean(data)
end
# 使用函数
data = [3, 7, 2, 9, 5, 1, 8]
[minV, maxV, meanV] = stats(data)
disp("最小值:" + num2str(minV)) # 1
disp("最大值:" + num2str(maxV)) # 9
disp("平均值:" + num2str(meanV)) # 5
示例:求解二次方程
function [x1, x2] = solveQuadratic(a, b, c)
# 求解 ax^2 + bx + c = 0
discriminant = b^2 - 4*a*c
if discriminant < 0
disp("无实数解")
x1 = nan
x2 = nan
else
x1 = (-b + sqrt(discriminant)) / (2*a)
x2 = (-b - sqrt(discriminant)) / (2*a)
end
end
# 使用函数
[root1, root2] = solveQuadratic(1, -3, 2)
disp("根1:" + num2str(root1)) # 2
disp("根2:" + num2str(root2)) # 1
只接收部分返回值
# 只接收第一个返回值
area = rectangle(5, 3) # 只获取面积
# 使用占位符 ~ 忽略某些返回值(如果支持)
[~, maxV, ~] = stats(data) # 只获取最大值
多个输入参数
函数可以接受任意数量的输入参数:
基本示例
function result = add(a, b)
result = a + b
end
function result = add3(a, b, c)
result = a + b + c
end
function result = multiply(a, b)
result = a * b
end
# 使用
x = add(3, 5) # 8
y = add3(1, 2, 3) # 6
z = multiply(4, 7) # 28
示例:线性函数
function y = linear(x, slope, intercept)
y = slope * x + intercept
end
# 使用
x = 1:10
y = linear(x, 2, 3) # y = 2x + 3
匿名函数
使用 @(参数) 表达式 语法创建匿名函数(也称 lambda 函数)。匿名函数会自动捕获定义时的外部变量(闭包):
基本语法
% 单参数匿名函数
f = @(x) x^2 + 1
y = f(3) # 10
# 多参数匿名函数
g = @(x, y) x^2 + y^2
z = g(3, 4) # 25
# 无参数匿名函数
pi_func = @() 3.14159265358979
val = pi_func() # 3.14159...
闭包:捕获外部变量
% 匿名函数在创建时捕获外部变量的当前值
a = 2
b = 3
linear = @(x) a * x + b # 捕获 a=2, b=3
y1 = linear(5) # 2*5+3 = 13
# 即使之后修改 a/b,捕获的值不变
a = 10
y2 = linear(5) # 仍然是 13(快照值)
作为参数传递
% 将函数传给另一个函数(高阶函数)
function result = applyFunc(f, x)
result = f(x)
end
double_it = @(x) x * 2
square_it = @(x) x^2
r1 = applyFunc(double_it, 5) # 10
r2 = applyFunc(square_it, 5) # 25
# 牛顿法的函数句柄传递
root = newtonMethod(@myFunc, @myFuncDerivative, 1.0, 1e-6, 100)
函数句柄与已命名函数
使用 @name 创建一个指向已命名函数(内建函数或用户自定义函数)的句柄。句柄可以存入变量、传入其他函数,或直接调用:
# 指向内建函数
f = @sin
disp(f(pi / 2)) # 1.0
disp(f(0)) # 0.0
# 指向用户自定义函数
function y = square(x)
y = x * x
end
g = @square
disp(g(5)) # 25
# 将函数句柄传入高阶函数
function result = apply_twice(f, x)
result = f(f(x))
end
disp(apply_twice(@square, 3)) # square(square(3)) = square(9) = 81
# 与匿名函数等价写法对比:
h = @(x) x * x # 效果与 @square 相同
disp(h(5)) # 25
nargin / nargout
在函数体内,nargin 表示实际传入的参数数量,nargout 表示调用方请求的返回值数量。常用于实现可选参数:
基本用法
function result = myFunc(a, b, c)
disp("nargin = " + num2str(nargin))
if nargin < 2
b = 10 # 默认值
end
if nargin < 3
c = 100 # 默认值
end
result = a + b + c
end
r1 = myFunc(1) # nargin=1, result=111
r2 = myFunc(1, 2) # nargin=2, result=103
r3 = myFunc(1, 2, 3) # nargin=3, result=6
根据 nargout 决定计算量
function [mn, mx, avg] = analyze(data)
mn = min(data)
if nargout >= 2
mx = max(data) # 只在需要时才计算
end
if nargout >= 3
avg = mean(data)
end
end
data = [1 2 3 4 5]
mn = analyze(data) # 只计算 min
[mn, mx] = analyze(data) # 计算 min 和 max
[mn, mx, av] = analyze(data) # 全部计算
在顶层脚本中,nargin 和 nargout 的值为 -1(表示不在函数调用上下文中)。
变量作用域
局部变量
在函数内部定义的变量是局部变量,只在函数内部可见:
function result = myFunc(x)
temp = x * 2 # temp 是局部变量
result = temp + 1
end
# 在函数外部无法访问 temp
y = myFunc(5) # y = 11
# disp(temp) # 错误:temp 未定义
参数作用域
函数参数在函数内部作为局部变量:
function result = modify(x)
x = x + 10 # 修改局部副本
result = x
end
value = 5
result = modify(value)
disp(value) # 仍然是 5(原值未改变)
disp(result) # 15
全局变量访问
函数可以访问外部作用域的变量(闭包特性):
global_var = 100
function result = useGlobal(x)
result = x + global_var # 可以读取外部变量
end
y = useGlobal(10) # 110
递归函数
函数可以调用自身:
阶乘(递归版)
function result = factorial(n)
if n <= 1
result = 1
else
result = n * factorial(n - 1)
end
end
# 使用
fact5 = factorial(5) # 120
fact10 = factorial(10) # 3628800
斐波那契数列
function result = fibonacci(n)
if n <= 2
result = 1
else
result = fibonacci(n-1) + fibonacci(n-2)
end
end
# 使用
for i = 1:10
disp("fib(" + num2str(i) + ") = " + num2str(fibonacci(i)))
end
最大公约数(欧几里得算法)
function result = gcd(a, b)
if b == 0
result = a
else
result = gcd(b, mod(a, b))
end
end
# 使用
g = gcd(48, 18) # 6
disp("GCD: " + num2str(g))
实用函数示例
判断素数
function result = isPrime(n)
if n < 2
result = 0
return
end
for i = 2:sqrt(n)
if mod(n, i) == 0
result = 0
return
end
end
result = 1
end
# 使用
if isPrime(17)
disp("17 是素数")
end
向量归一化
function normalized = normalize(vec)
magnitude = sqrt(sum(vec .^ 2))
normalized = vec / magnitude
end
# 使用
v = [3, 4]
v_norm = normalize(v) # [0.6, 0.8]
disp("归一化后的向量:")
disp(v_norm)
计算两点距离
function d = distance(x1, y1, x2, y2)
d = sqrt((x2 - x1)^2 + (y2 - y1)^2)
end
# 使用
dist = distance(0, 0, 3, 4) # 5
disp("距离:" + num2str(dist))
线性插值
function y = lerp(a, b, t)
# 在 a 和 b 之间线性插值,t ∈ [0, 1]
y = a + (b - a) * t
end
# 使用
y = lerp(0, 10, 0.5) # 5 (中点)
y = lerp(0, 10, 0.25) # 2.5
矩阵转换
function B = rotateMatrix90(A)
# 将矩阵顺时针旋转90度
B = A'
B = fliplr(B)
end
# 使用
A = [1 2 3; 4 5 6; 7 8 9]
B = rotateMatrix90(A)
disp("旋转后的矩阵:")
disp(B)
高级示例:牛顿法求根
function root = newtonMethod(f, df, x0, tolerance, maxIter)
# f: 函数(传入函数句柄或使用字符串)
# df: 导数
# x0: 初始猜测
# tolerance: 容差
# maxIter: 最大迭代次数
x = x0
for iter = 1:maxIter
fx = f(x)
dfx = df(x)
if abs(fx) < tolerance
root = x
disp("收敛于 " + num2str(iter) + " 次迭代")
return
end
x = x - fx / dfx
end
root = x
disp("达到最大迭代次数")
end
# 定义要求根的函数和导数
function y = myFunc(x)
y = x^2 - 2 # 求 sqrt(2)
end
function y = myFuncDerivative(x)
y = 2*x
end
# 使用牛顿法
root = newtonMethod(@myFunc, @myFuncDerivative, 1.0, 1e-6, 100)
disp("sqrt(2) ≈ " + num2str(root))
函数最佳实践
1. 使用描述性的函数名
# 好的命名
function area = calculateCircleArea(radius)
function [min, max] = findMinMax(data)
# 避免的命名
function a = f(r)
function [x, y] = calc(d)
2. 添加注释说明
function [area, perimeter] = rectangle(width, height)
# 计算矩形的面积和周长
# 输入:
# width - 矩形宽度
# height - 矩形高度
# 输出:
# area - 面积
# perimeter - 周长
area = width * height
perimeter = 2 * (width + height)
end
3. 参数验证
function result = safeDivide(a, b)
# 安全除法,检查除数
if b == 0
disp("错误:除数不能为零")
result = nan
else
result = a / b
end
end
4. 单一职责原则
每个函数应该只做一件事,并把它做好:
# 好的设计:功能单一
function area = calculateArea(radius)
area = pi * radius^2
end
function circumference = calculateCircumference(radius)
circumference = 2 * pi * radius
end
# 而不是把所有功能塞在一个函数里