akaneutils.lua

akaneutils.lua

ユーティリティ関数集です。

-----------------------------------------------------------
-- @file  akaneutils(lua)
-- @brief 茜ちゃんによるユーティリティ集や
-- @author 琴葉茜(さとうけい)
-----------------------------------------------------------

-----------------------------------------------------------
-- 引数なしの関数を作るよ<br>
-- CCのparallel APIに渡したりするのに便利やで
--
-- @param func 元の関数
-- @param ... 関数に渡す引数
-- @return 作った関数
-----------------------------------------------------------
function getNoArgFunc(func, ...)
  local args = { ... } -- ...のままではクロージャで参照できんからテーブルに入れるよ
  if #args < 1 then
    return func -- 引数がなければそのまま返す
  end
  return function()
    return func(unpack(args)) -- argsを参照できるのはクロージャだからやで
  end
end


-----------------------------------------------------------
-- テーブルを上書きするよ<br>
-- baseをoverで上書きするよ
-- luaのテーブルは参照渡しだから、関数呼び出し元の方のテーブルも上書きされてるよ
--
-- @param base 上書きされるテーブル
-- @param over 上書きするテーブル
-----------------------------------------------------------
function overwrite(base, over)
  for k, v in pairs(over) do
    base[k] = v -- テーブル上書きや! 以上!
  end
end

-----------------------------------------------------------
-- テーブルにイベントハンドラを追加するよ<br>
-- 追加前に登録されてたハンドラもちゃんと呼ぶよ
-- いまんとこ追加されたハンドラを削除する方法は無いよ
-- CCのイベントシステム使わん場合の簡易な方法やな
-- @param table [in/out]ハンドラを登録するテーブル
-- @param key [in]登録対象のキー
-- @param handler [in]登録するイベントハンドラ
-----------------------------------------------------------
function addHandler(table, key, handler)
  local org_handler = table[key]
  local new_handler = handler
  if type(org_handler) == 'function' then -- すでにハンドラが登録されてたら
    new_handler = function(...) -- 元のハンドラと追加するハンドラ両方呼ぶ関数を作るよ
      org_handler(...)
      handler(...)
    end
  end
  table[key] = new_handler -- ハンドラ登録や!
end

-----------------------------------------------------------
-- ファイルを書き込む準備をするよ
-- いまんとこ具体的には必要なディレクトリがなければ作る、ってだけやけどな
-- @param path [in]書き込む予定のファイルのパス
-----------------------------------------------------------
function prepareToWrite(path)
  local dir = fs.getDir(path)
  if not fs.exists(dir) then -- ディレクトリが存在するか見るで
    fs.makeDir(dir) -- 無いから作るよ
  end
end

-----------------------------------------------------------
-- 文字列をセパレータで分割する、イテレータ関数を作って返すよ
-- セパレータが省略されると','を使うよ
-- @param str [in]分割する文字列
-- @param separator [in]セパレータ・区切り文字。省略時は','
-- @return イテレータ関数
-----------------------------------------------------------
function stringSplitter(str, separator)
  separator = separator or ',' -- 省略時はカンマを使うで
  local init = 1
  return function()
    if not init then return nil end -- 終わりや
    local sep_head, sep_tail, col_head, col_tail

    sep_head, sep_tail = string.find(str, separator, init, true) -- セパレータを探すよ
    if sep_head then -- 見つかったか?
      col_head, col_tail = init, sep_head - 1 -- 見つかったよ!
      init = sep_tail + 1 -- 次はセパレータの次の文字から探すよ
    else
      col_head, col_tail = init, -1 -- 見つからんかった
      init = nil -- 次はもうないよ
    end
    return string.sub(str, col_head, col_tail) -- 分割や!
  end
end

-----------------------------------------------------------
-- セパレータで区切られた文字列を分割するよ
-- セパレータが省略されると','を使うよ
-- strがnilの場合はnilを返すよ
-- @param str [in]分割する文字列
-- @param separator [in]セパレータ・区切り文字。省略時は','
-- @return 分割された文字列を格納した配列
-----------------------------------------------------------
function splitString(str, separator)
  if not str then return nil end
  local result = {} -- 結果を入れる配列や

  for column in stringSplitter(str) do -- 分割するよ
    result[#result + 1] = column
  end
  return result
end

-----------------------------------------------------------
-- テキストファイルを読んで1行ずつ返す、イテレータ関数を作って返すよ
-- 第二戻り値でopen中のファイルハンドルも返すで
-- ファイルの最後まで読み込まないとファイルクローズしないから
-- forの途中で抜け出す場合は、そっちでファイルクローズしないとあかんよ
-- @param path [in]csvファイルのパス
-- @return イテレータ
-- @return ファイルハンドル
-----------------------------------------------------------
function fileEachLine(path)
  -- ファイルがなければnilを返すだけの関数を返すよ
  if not fs.exists(path) then return (function() return nil end) end

  local h = fs.open(path, 'r')
  assert(h, 'fileEachLine cant open : '.. path)

  -- イテレータ関数
  local function iterator()
    local line = h.readLine() -- 次の行を読むよ
    if not line then -- ファイルはもう終わりか?
      h.close()
      h = nil
    end
    return line, h
  end

  return iterator, h
end

-----------------------------------------------------------
-- csvファイルを読んでカラムの配列を返す、イテレータ関数を作って返すよ
-- ファイルのクローズに関してはfileEachLine()と同じやで
-- セパレータが省略されると','を使うよ
-- @param path [in]csvファイルのパス
-- @param separator [in]セパレータ・区切り文字。省略時は','
-- @return イテレータ
-- @return ファイルハンドル
-- @see fileEachLine
-----------------------------------------------------------
function csvEachLine(path, separator)
  separator = separator or ','

  local each_line, h = fileEachLine(path)

  -- イテレータ関数
  local function parser()
    return splitString(each_line(), separator), h
  end

  return parser, h
end