上面一遍文章讲了函数,但是没有讲完,要是一股脑都放在一片文章里,篇幅有点长,所以这里分成两部分。
下面继续讲python和lua中的函数概念。
函数多返回值的区别
python和lua函数提供了一种非常规的机制,但很方便,函数可以一次返回多个值。
python
def foo0():pass # 无返回
...
def foo1():return "a" # 返回1个
...
def foo2():return "a","b" # 返回2个
...
lua
function foo0 () end -- 无返回
function foo1 () return 'a' end --返回1个
function foo2 () return 'a','b' end--返回2个
看下面的例子,注意区分两者的不同之处:
lua lua的特性比较多,所以这一次把lua的示例提前。
x,y = foo2() -- x='a', y='b'
x = foo2() -- x='a','b'被舍弃
x,y,z = 10,foo2()-- x=10, y='a', z='b'
x,y = foo0() -- x=nil, y=nil
x,y = foo1() -- x='a', y=nil
x,y,z = foo2() -- x='a', y='b', z=nil
print(foo2() .. "x") --打印 ax 当函数调用出现在表达式中的时候,Lua只取了一个返回值用来做运算,所以只看到 ax
python
x,y = foo2() # x='a', y='b'
x = foo2() # x=('a', 'b') ,这里区别很大
x,y,z = 10,foo2()# 报错
x,y = foo0() # 报错
x,y = foo1() # 报错
x,y,z = foo2() # 报错
print(foo2() .. "x") #报错
仔细注意python和lua的区别!
其实python对多返回值只是把它们打包成tuple元组(只读),如果你要修改那么会报错:
>>> x[0] = "c"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
lua在这方面更胜一筹,各有各的特色嘛。
函数返回值也能构造一个对象
lua
a = {foo0()} -- a = {} (an empty table)
a = {foo1()} -- a = {'a'}
a = {foo2()} --[[ a = {'a', 'b'} 这种取到多个返回值的只能是foo2放在最后一个,
如果像下面,foo2不是最后一个,那么只能取第一个返回值。
--]]
a = {foo0(), foo2(), 4} -- a[1] = nil, a[2] = 'a', a[3] = 4
python
a = [foo0()] # a = [None] 包含一个None类型的列表
a = [foo1()] # a = ['a']
a = [foo2()] # a = [('a', 'b')] ,这里区别很大
a = [foo0(), foo2(), 4] # a[0] = None, a[1] = ('a','b'), a[2] = 4
我们还可以像下面这样定义lua函数~
function foo (i)
if i == 0 then return foo0()
elseif i == 1 then return foo1()
elseif i == 2 then return foo2()
end
end
print(foo(1)) --> a
print(foo(2)) --> a b
print(foo(0)) -- (no results)
print(foo(3)) -- (no results)
--可以通过加括号的方式,强制只返回一个值。在python中则没什么卵用!
print((foo2())) --> a
python
>>> def foo(i):
... if i == 0 : return foo0()
... elif i == 1 : return foo1()
... elif i == 2 : return foo2()
...
>>>
>>>
>>> print(foo(1))
a
>>> print((foo(2))) # 加了括号,还是一样的输出
('a', 'b')
>>> print(foo(0))
None
>>> print(foo(3))
None
更多关于python函数特性
在讲python函数的更多之前,我们先看看python的Lambdas
它的语法是 lambda arguments : expression
,它能帮我们生成一个匿名函数对象,函数呢类似下面这样:
def <lambda>(arguments):
return expression
知晓上面的语法后,我们就可以很简单的创造函数了。
python
>>> def make_incrementor(n): # 返回一个函数对象
... return lambda x: x + n
...
>>> f = make_incrementor(42) # 这里指定 n = 42
>>> f(0) # 我们传入 x=0
42
>>> f(1) # 我们传入 x=1
43
python 函数参数注解,虽然python提供了动态类型变量,但是一般来说我们的函数只能处理固定类型的数据,不可能变来变去。所以有了这个东东:
python
>>> def f(ham: str, eggs: str = 'eggs') -> str:
... print("Annotations:", f.__annotations__)#打印注解
... print("Arguments:", ham, eggs)
... return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
Arguments: spam eggs
'spam and eggs'
在函数申明的时候,ham冒号后面指定了我的类型为str
,egg也指定类型为str
,并且给了默认参数'eggs'
, -> str
表示这个函数返回字符串。
更多关于lua函数特性
lua可变长参数
printResult = ""
function print (...) -- 三个点表示函数接受可变长参数
for i,v in ipairs({...}) do -- 记得以前版本是可以直接用arg关键字的,但是现在只有这个{...},{...} 记录所有传入的参数,#{...}记录参数数量
printResult = printResult .. tostring(v) .. "\t"
end
printResult = printResult .. "\n"
end
在讲固定参数和不定参数前先举个其他的例子:
大多数时候,我们都是用函数的第一个返回值的,但是有时候我们只用函数的第二个返回值类似:
local _, x = string.find(s, p)-- 只用第二个返回值 x
一个替代的方法是:
print(string.find("hello hello", " hel")) -->6 9
print(select(1, string.find("hello hello", " hel")))-->6 9
-- 上面那个select,一开始用的时候让我吃惊,说好的取一个返回值的。。。
-- 不过大家记住就好了,上面select 1 其实没卵用。
print(select(2, string.find("hello hello", " hel")))--> 9
仔细观察select函数的参数,发现select就是这样一个函数,第一个固定参数,后面的不定。
类似的,我们可以申明这样的函数:function g (a, b, ...) end
调用g之后:
g(3) 实际参数: a=3, b=nil, {...}={}
g(3, 4) 实际参数: a=3, b=4, {...}={}
g(3, 4, 5, 8) 实际参数: a=3, b=4, {...}={5, 8}
上面例子的最好实践就是格式化输出
function fwrite (fmt, ...)
return io.write(string.format(fmt, table.unpack({...})))--使用table.unpack直接解包所有参数
end
非全局lua函数,
Lib = {}
Lib.foo = function (x,y) return x + y end
Lib.goo = function (x,y) return x - y end
等同于:
Lib = {
foo = function (x,y) return x + y end,
goo = function (x,y) return x - y end
}
还有另一种相同结果的语法:
Lib = {}
function Lib.foo (x,y)
return x + y
end
function Lib.goo (x,y)
return x - y
end
申明局部函数(非代码语言)
local f = function (...)
...
end
local g = function (...)
...
f() -- external local `f' is visible here
...
end
--另一种申明方法:
local function f (...)
...
end
在使用local关键字的时候需要注意递归写法。
local fact = function (n)
if n == 0 then return 1
else return n*fact(n-1) --这里报错,因为我们的fact这时候还是未定义的
end
end
-- 修复方法一
local fact -- 提前定义
fact = function (n)
if n == 0 then return 1
else return n*fact(n-1) -- 递归调用
end
end
-- 修复方法二
local function fact (n)
if n == 0 then return 1
else return n*fact(n-1)
end
end
-- 下面的例子,必须先申明f,g
local f, g -- 提前申明
function g ()
... f() ... --相互调用
end
function f ()
... g() ... --相互调用
end
写lua函数的时候尽量使用 Tail Call:
lua
function f (x) -- 最简单的tail call例子
return g(x)--f调用g之后,不需要做任何事,这样就不需要保留g的返回信息在当前函数(f)栈中,类似这样的调用我们说它们支持tail calls,就像lua解释器一样
end
写在函数最后的话:
代码风格
- 使用4个空格缩进,而非tab
- 一行不要超过79个字符
- 函数和类(以后会介绍)的上下行空开
- 尽量多的写代码注释
- 使用文档字符串,仅python
- 操作符前后空格,逗号后空格
- 保持命名一致。类(后面会介绍)命名:
CamelCase
,函数方法命名:lower_case_with_underscores
- 使用UTF8编码
- 不要使用ASCII以外的表情符号。
未完待续。。。