本文同步發表於個人網站
Lua函數可以返回多值。在我看來,這個特性是特殊的,只有少數語言真正做到多值返回。什麼意思?這表示在接收一個函數的返回值,可以輕易忽略掉主要值以外的結果。這些結果一般用於輔助,絕大多數時候,我們不關心、不需要,但有時候非常有作用。
先來看看Python裡的dict.get:
_d = {}
_v = _d.get("key", "value")
print(_v) # => Output: value
大多數時候,並不關心_v
的值是否真的從_d
來的,還是其實是個預設值。如果我們想要確定,那就要在多寫一次:
found = "key" in _d
我們可以替Lua寫一個相似的函數,但其還多返回一個用於解釋是否是有找到的值:
function get(dict, key, default)
if dict[key] ~= nil then
return dict[key], true
else
return default, false
end
end
其實可以在簡化一些:
function get(dict, key, default)
return (dict[key] or default), dict[key] ~= nil
end
這麼一來,平常可以這樣用:
_d = {}
_v = get(_d, "key", "value")
print(_v) -- => Output: value
如果還需要而外知道是否是從_d
找到的, 可以這樣寫:
_d = {}
_v, found = get(_d, "key", "value")
print(_v) -- => Output: value
print(found) -- => Output: false
這樣就很像Common Lisp裡面,gethash
的行為:
(defvar *h* (make-hash-table :test 'equal))
(multiple-value-bind
(_v found) (gethash 'key *h* "value")
(print _v) ;; => Output: value
(print found)) ;; => Output: Nil
與Python相比,Python的多值返回比較像是ES6的解構賦值。實際上他是返回一個Tuple
。
def get(_d:dict, key, default):
return _d.get(key, default), key in _d
_d = {}
_v, found = get(_d, "key", "value")
print(_v, found) # => value True
_v = get(_d, "key", "value")
print(_v) # => ('value', False)
第二次的_v
,並不如預期只接受主要返回值"value"
,而是整個返回值("value", False)
依我理解,Go語言中也有多值返回。不過如果不使用返回值,必須顯式忽略:
package main
import (
"fmt"
)
func main() {
v, _ := get()
fmt.Println(v)
}
func get() (int, bool) {
return 1, true
}
當中get
的第二個返回值如果不使用,必須使用佔位_
來明確示不使用。
少數的例外可能只有內建的Map
取值,這與上例的行為一致:
package main
import (
"fmt"
)
func main() {
m := make(map[string]int)
v := m["key"]
fmt.Println(v) // Output: 0
}
map會返回預設的零值,整數的零值就是0
。此外其實還會返回一個是否查找到的值:
package main
import (
"fmt"
)
func main() {
m := make(map[string]int)
v, ok := m["key"]
fmt.Println(v, ok) // Output: 0 false
m["key"] = 50
v, ok = m["key"]
fmt.Println(v, ok) // Output: 50 true
}
相關可以參考官方文件。
在函數一章,已經說過不定長參數,也提到過函數呼叫的語法糖。Lua沒有具名參數,不能像Python那樣明確指定參數名稱。不過透過語法糖仍然可以做到類似的功能。
function Person(info)
local person = {}
person.name = info.name or "Bob"
person.age = info.age or 20
person.address = info.address or ""
return person
end
p1 = Person {
name = "Tina",
age = 13,
address = "secret"
}
print("Name: ", p1.name)
print("Age: ", p1.age)
print("Address: ", p1.address)
Name: Tina
Age: 13
Address: secret
這樣語法糖的寫法,還可以混合著位置參數:
function Person(args)
local person = args
person.name = args.name or "Bob"
person.age = args.age or 20
person.address = args.address or ""
return person
end
p1 = Person {
"位置參數1",
age = 13,
address = "secret",
"位置參數2",
}
print("Name: ", p1.name)
print("Age: ", p1.age)
print("Address: ", p1.address)
print("-------------")
print("位置參數:", p1[1], p1[2])
Name: Bob
Age: 13
Address: secret位置參數: 位置參數1 位置參數2