尾端的 Nil 參數

lua-users home
wiki


[!] VersionNotice:以下程式碼涉及較舊的 Lua 版本,Lua 4。在 Lua 5 下執行時無法正常執行。

在純 Lua 中,傳遞 nil 引數等於省略此引數。然而,從 Lua 呼叫的 C 函數可以偵測提供給它們的引數數量(包括 nil!)。這常常造成混淆和產生難看的程式碼。

在我看來,lua 標準函式庫應清除,不要使用傳遞的引數數量來決定如何運作。

strfind(s,exp) 應該導致與下列結果相同:strfind(s,exp,nil,nil)

然後,你可以透過使用最大引數數量為任何函數撰寫封裝器

function mystrfind(a,b,c,d)
    -- do some extra handling here...
    return strfind(a,b,c,d)
end

範例(ReubenThomas 在 lua-l 論壇的信件)

如果我執行下列指令碼

t = {}
tinsert(t, 3, "hello", 1)
print(t[3])

我在預期 hello 時,會得到 1

[...] 我因為試著執行下列形式的陳述而受到此影響
tinsert(t, n, gsub(...))

而得到的卻是取代次數,而不是已插入的取代結果。這一定有錯吧?

現在我重新閱讀時,我發現這與尾端的 nil 問題相反 - lua 函數會忽略額外的引數,而 c 函數會收到所有的引數(而且 tinsert 會將其最後一個參數插入) - 最好將此頁面標題定為「lua 函數和 c 函數在引數處理上有不同處理方式所造成的問題」 -- PeterPrade

然而,在後續討論中,當人們試圖修正 tinsert 的異常時,遇到了尾端的 nil 問題 - 他們最後必須發現你無法為 lua 中的 tinsert 撰寫適當的封裝器函數。讓我引用 ET 對該討論串的最後訊息

[...] 相容(已修正)的版本應為

function tinsert(t, ...)
  if arg.n == 1 then
    %tinsert(t, arg[1])
  elseif arg.n == 2 then
    %tinsert(t, arg[1], arg[2])
  else
    error("wronger number of args ("..arg.n+1..") for tinsert");
  end
end
但這仍然會對下列狀況產生意外的結果

tinsert(t, foo()) --> 可能產生 tinsert(t,a,b)

tinsert(t, x, foo()) --> 可能產生 tinsert(t, x)

當 foo 沒有回傳預期的結果數目時。

我長久以來都提倡讓 C 函數在引數處理方面與 Lua 函數有相似的表現(讓遺漏的引數為 nil)。依我之見,tinsert 特別是一個很好的例子,說明會發生什麼事...

Ciao,ET。
另一個範例見於 FileInputOutput 頁面 - 難看的程式碼,因為 readfrom(nil) 會導致錯誤(而 readfrom() 完全合法)

function readfrom(f)
  local h, err
  if f then h, err = %readfrom(f)
  else      h, err = %readfrom()
  end
  ...

郵件列表中出現另一個範例

> I just noticed that contrary to what you might expect, if you 
> pass "nil" as the fourth argument of strfind, it does a plain match. 
> Looking at the source, str_find indeed simply looks to see whether it
> has a fourth argument.

Sigh, another example of the arg handling ad hockery...

> This is rather annoying, as in some other cases where it's nice to be
> able to pass "nil" instead of omitting the argument.

You say it...

> It would not be contrary to the 4.0 manual to change this behaviour
>[...]
> Could this be done?

Sure, it _could_.  One could fix this place by converting the test
lua_gettop(L)>3  to  luaL_opt_int(L,3,0)==1,  or one could finally
start thinking about cleaning up argument handling in the C-API...

>[...] is there any way around this?

function strfind(a,b,c,d)
  c = c or 1  -- c has a similar problem...
  if d then
    return %strfind(a,b,c,d)
  else
    return %strfind(a,b,c)
  end
end

在這裡加入更多範例...


RecentChanges · preferences
edit · history
最後編輯:格林威治時間 2007 年 7 月 7 日,下午 8:23 (diff)