2017-04-14 4 views
3

私は渡された変数から空でない最初の文字列を返す関数を実装しようとしています。 最初の非nil、空でない文字列を返す関数を作成するにはどうすればよいですか?

function first_non_empty(...) 
    for i, item in ipairs({...}) do 
     if item ~= nil and item ~= '' then 
      return item 
     end 
    end 
    return '' 
end 

が動作しない素朴なアプローチので、残念ながら、これらの変数のいくつかは、nilに次のようになります。それがnil値を検出したとしてipairsとすぐ出て終了します。これは、変数をゼロにすることができないように要件を変更するか、長さを関数に渡してテーブル長を ipairsに依存させる必要がないようにするか、関数内のすべてのパラメータをラップしてそれらは明示的にゼロである。

function first_non_empty_func(...) 
    for i, func in ipairs({...}) do 
     local item = func() 
     if item ~= nil and item ~= '' then 
      return item 
     end 
    end 
    return '' 
end 

function fn(p) 
    local f = function() return p end 
    return f 
end 

-- change callers to first_non_empty_func(fn(a), fn(b), fn(c)) 

しかし、これらのソリューションの両方が関数プロトタイプを複雑にしている。パラメータの順序付けられたリストを取る関数が存在しますが、そのうちのいくつかはnilでなくてもよく、空でない文字列ではない最初のパラメータを返します。

function first_non_empty_pack(...) 
    local t = table.pack(...) 
    for i = 1, t.n do 
     local item = t[i] 
     if item ~= nil and item ~= '' then 
      return item 
     end 
    end 
    return '' 
end 

答えて

3

select('#', ...)はので、ここで、提供された引数の数を取得するために使用することができますtable.packを使用しない代替手段です:

function first_non_empty_pack(...) 
    for i = 1, select('#', ...) do 
     local item = select(i, ...) 
     if item ~= nil and item ~= '' then 
      return item 
     end 
    end 
    return '' 
end 
+0

ニース!ボーナスとして、 'select'が5.1に存在し、' table.pack'はそうではないので、これをScribuntoで使うことができます。 –

+0

@ChrisMidgley 'function table.pack(...)return {n = select( '#'、...)、...} end'はすべてです。長いvarargリストの場合、 'select'呼び出しのためにリストを繰り返しコピーすることは、一度テーブルを作成してそれを反復するよりも遅くなるかもしれません。他の方向では、あとでガベージコレクションする必要があるテーブルを作成するのが遅くなる可能性があります。だからベンチマークはあなたのケースでどちらが速いかを知ることができます(あるいは速度が実際問題であることがわかるまでクリーナーだと思うものを選ぶだけです)。 – nobody

4

使用table.pack、。余分なテーブルが作成されていないなど:

function first_non_empty(item, ...) 
    if item ~= nil and item ~= '' then return item end 
    return first_non_empty(...) 
end 

ただし、リストには終了マーカーがいくつかあります。たとえば、boolean 'false'は、非ゼロの空でない空の文字列がないことを示します。

1

単純なアプローチは、再帰を使用することである:すべてのゼロのエントリを保持し、nフィールド内のエントリの数を返し

+0

エンドマーカーが(「#」を選択し、 '、必要とされていません.. == 0 ')を停止条件として用いることができる。繰り返される 'select'呼び出しを避ける代替手段は、最初にカウントを決定し、' do local function rec(n、item、...)のようにカウントダウンすることです。item〜= nilとitem〜= ''ならばitem elseif (n-1、...)end end function first_non_empty(...)rec(select( '#'、...)、...)end end'を返します。 – nobody

+0

終了マーカーがあると、この機能がより迅速かつ簡単になります。また、終了マーカーはブール値ではなく、他の文字列が見つからない場合に返される一部のフォールバック文字列値でもかまいません。これはこの機能の外でいくつかの小切手を節約します。それはすべて実際のニーズに依存しており、この機能は選択肢の1つに過ぎません:) – Vlad

関連する問題