よく使う関数をモジュール化してみる
Lua言語でiPhoneとAndroidの両方のプログラムが作れるCoronaという開発ツールがありまして、今週末、「日本Coronaの会 関西 第一回 初心者向け勉強会」というものが開催されます。
同じLua言語の開発ツールなので、Coronaにも興味がありまして、参加申し込みしました。
せっかく参加するので、何かLuaのプログラムを作ろうかなと思っていたのですが、Luaridaのプログラムで、Luaridaに依存しないプログラムはCoronaでも動くのではないかと思い、じゃ、そういうのをモジュールにしてみようと思って作ってみました。
Luaのモジュール化
Luaのモジュールは、普通にLuaのプログラムを作って、それを別のLuaプログラムがrequireで呼び出すだけでできるのですが、それだと、モジュール内と呼び出しプログラムで関数名が被っているとまずいことになります。そこで、モジュール側にモジュール宣言を行って、関数名の被りを避けられるようにします。
--モジュール宣言------------------------- module( "xmlt", package.seeall )
moduleの第1引数は、モジュールのテーブル名です。このテーブルに関数が作られるので、関数の呼び出しは、例えば、xmlt.foo()とかになります。
moduleの第2引数は、module宣言をしてしまうと、元々ある関数 math.なども宣言しないと呼び出せなくなってしまうので、package.seeall と引数をつけると、元々ある関数は宣言しなくても呼び出せるようになります。
モジュールサンプル
Luaridaでモジュールを使うサンプルを作ってみたいと思います。
luaridamm.luaというモジュールを作ります。
--モジュール宣言---luaridamm.lua---- module( "mm", package.seeall ) function sqx( x ) return x*x end
このモジュールは、sdcard/luaridaフォルダに保存するとします。関数 sqx を呼び出すときには、mm.sqx( 2 )のようになります。
モジュールを呼び出す側のサンプルを書きます。package.pathにモジュールの場所を指定することにより、require でモジュールを呼び出すことができます。?.luaとなっているのは、"何とか.lua"というモジュールファイルを複数置きたいときに使えます。
--モジュール読み込み宣言 package.path = system.getCardMnt().."/luarida/?.lua" require( "luaridamm" ) x = mm.sqx( 6 ) dialog( "6*6", "="..x,1 ) system.exit()
簡単なXMLパーサのモジュールを作ってみました
以前、気象庁のデータを読み込むプログラムを作りましたが、これのXMLパーサ部分はLuaridaに依存しないプログラムなので、これをモジュール化してみたいと思います。そして、Coronaの勉強会に持って行きたいと思いますが、まぁきっと既にあるのでしょうね(^^;。
下記にモジュール化したプログラムを書きます。(追記、モジュール内でローカルに使う関数は、localを付けて外から見えなくしました)
------------------------------------------ --XMLパーサモジュール(xml_t_module.lua) ------------------------------------------ --モジュール宣言------------------------- module( "xmlt", package.seeall ) --関数宣言-------------------------------- split={} --文字の分解 commentSkip={} --コメントをスキップします skipSpc={} --スペースやタブを読み飛ばします tagTopFound={} --記号(kigou)に指定された文字が来るまで読み込みます getTag={} --タグの要素を配列に入れて返し、データも取得して返します ------------------------------------------ --文字の分解 ------------------------------------------ local function split(str, d) local s = str local t = {} local p = "%s*(.-)%s*"..d.."%s*" local f = function(v) table.insert(t, v) end if s ~= nil then string.gsub(s, p, f) f(string.gsub(s, p, "")) end return t end ------------------------------------------ --コメントをスキップします --">"文字を読み込んで終了します --nilが返れば失敗しています ------------------------------------------ local function commentSkip( fp ) local str = fp:read( 1 ) if( str=="-" )then str = fp:read( 1 ) if( str=="-" )then -- "!--"だったので、"-->"が来るまで進む while(true)do str = fp:read( 1 ) if( str==nil )then break end if( str=="-" )then str = fp:read( 1 ) if( str==nil )then break end if( str=="-" )then str = fp:read( 1 ) if( str==nil )then break end if( str==">" )then break end end end end else -- ">" が来るまで進む while(true)do str = fp:read( 1 ) if( str==nil or str==">" )then break end end end elseif( str~=">" )then -- ">" が来るまで進む while(true)do str = fp:read( 1 ) if( str==nil or str==">" )then break end end end return str end ------------------------------------------ --スペースやタブを読み飛ばします --スペースやタブの次に来る一文字が戻ります --nilが返れば失敗しています ------------------------------------------ local function skipSpc( fp ) local str while(true)do str = fp:read( 1 ) if( str==nil )then break end str = string.gsub( str, "^%s*(.-)", "%1") if( str~="" )then break end end return str end ------------------------------------------ --記号(kigou)に指定された文字が来るまで読み込みます --nilが返れば失敗しています ------------------------------------------ local function tagTopFound( fp, kigou ) local b local str local s = string.byte( kigou ) while( true )do str = fp:read( 1 ) if( str==nil )then break end b = string.byte( str ) if( s==b )then break end end return str end ------------------------------------------ --タグの要素を配列に入れて返し、データも取得して返します ------------------------------------------ function getTag( fp, tagname ) local str local tbl={} local closeflg = false local data = "" while(true)do --記号(kigou)に指定された文字が来るまで読み込みます if( tagTopFound( fp, "<" )==nil )then break end --スペースやタブを読み飛ばします str = skipSpc( fp ) if( str==nil )then break elseif( str=="!" )then --コメントをスキップします if( commentSkip( fp )==nil )then break end else --タグ名をチェックします if( str==string.sub( tagname,1,1) )then local i local fitflg = true for i=1, #tagname do local n = string.sub( tagname,i,i) if( n~=str )then fitflg = false break end str = fp:read(1) if( str==nil )then fitflg = false break end end --タグ名が同じであれば、要素取得に移ります if( fitflg==true )then break end end end end --要素取得 local eldat = string.gsub( str, "^%s*(.-)", "%1") if( eldat=="" )then --スペースやタブを読み飛ばします eldat = skipSpc( fp ) end if( eldat~=">" )then -- ">"で無ければ、要素があると考える while(true)do -- ">"が来るまで要素を取得する str = fp:read(1) if( str==nil or str==">" )then break end eldat = eldat..str end --データの前後のスペースなどをトリムする eldat = string.gsub(eldat, "^%s*(.-)%s*$", "%1") -- "/>"があればデータが無いのでcloseflgをtrueにする if( eldat=="/" or string.sub( eldat,-1)=="/" )then closeflg = true end -- " で切り出す local g={} local value local i g = split( eldat, '"' ) local mae={} local usiro={} local j=1 for i,value in pairs(g) do if( string.find ( value, "=" )~=nil )then local aa={} aa = split( value, '=' ) mae[j] = string.gsub(aa[1], "^%s*(.-)%s*$", "%1") else usiro[j] = string.gsub(value, "^%s*(.-)%s*$", "%1") j = j + 1 end end for i=1, j-1 do if( mae[i]~=nil )then tbl[mae[i]] = usiro[i] end end end --closeflgがfalseなら、データを読む if( closeflg==false )then -- "<" が来るまで読む data = "" while(true)do str = fp:read( 1 ) if( str==nil or str=="<" )then break end data = data..str end end return tbl, data end
XMLパーサモジュールを呼び出すサンプル
XMLパーサモジュールを呼び出すサンプルを作ってみました。以前ブログに書いたものと同じですが、気象庁のサイトがShift-JISコードだったのものが、知らない間にBOM無しUTF-8に変わっていました。なので、プログラムも変更しました。
修正したプログラムを再掲載します。先に取得結果画像を貼っておきます。
------------------------------------------ --アメダスデータの取得 ------------------------------------------ --モジュール読み込み宣言 package.path = system.getCardMnt().."/luarida/?.lua" require( "xml_t_module" ) --xmlパーサ(xmlt.***) --関数宣言-------------------------------- main={} --mainメソッド gethtml={} -- hmtlを取得 getAmedasTable={} --アメダスのデータを取得します --グローバル変数宣言---------------------- Amedas = {} --アメダスのデータを取得するテーブル HttpUrl = "http://www.jma.go.jp/jp/amedas_h" --アメダスURL Gwide, Gheight = canvas.getviewSize() --画面サイズ取得 LuaridaPath = system.getCardMnt().."/luarida" --luaファイルを保存しているPath ------------------------------------------ --アメダスのデータを取得します ------------------------------------------ function getAmedasTable( fname ) local dtable = {} local str = nil local tbl={} local fp = io.open( LuaridaPath.."/"..fname, "r") if( not(fp) )then toast(LuaridaPath.."/"..fname.." が読み込めませんでした") return dtable end --アメダスのテーブル先頭(table.id="tbl_list")を検索する while(true)do tbl, str = xmlt.getTag( fp, "table" ) if( str==nil or tbl.id=="tbl_list" )then break end end if( str~=nil )then local j = 1 local i = 1 local k -- td.class="time left"が来るまでtdデータを読み込む dtable[j]={} while(true)do tbl, str = xmlt.getTag( fp, "td" ) if( str==nil or tbl.class=="time left" )then break end dtable[j][i] = str i = i + 1 end if( str~=nil )then -- gkが列の数 local gk = i - 1 for j=2,26 do dtable[j]={} for i=1,gk do if( j==2 and i==1 )then if( str==" " )then str="" end dtable[j][i] = str else tbl, str = xmlt.getTag( fp, "td" ) if( str==nil )then break end if( str==" " )then str="" end dtable[j][i] = str end end end end end io.close( fp ) return dtable end ------------------------------------------ -- hmtlを取得 ------------------------------------------ function gethtml( urldata ) http.get( HttpUrl.."/"..urldata, LuaridaPath.."/"..urldata ) while( http.status()==0 )do end return http.status() end ------------------------------------------ -- メインプログラム ------------------------------------------ function main() local i local j local num local a local hmtlname = "yesterday-" --画面を白にする canvas.drawCls( color(255,255,255) ) editsetText( "65042" ) --入力文字の初期化(和歌山) num, a = editText( "地域番号の入力", 1 ) if( num==nil or num=="" or a~=1 )then return end item.clear() item.add( "今日のデータ" ) item.add( "昨日のデータ" ) a = item.radio( "取得日を選んでください",1 ) if( a==1 )then hmtlname = "today-"..num..".html" else hmtlname = "yesterday-"..num..".html" end -- hmtlを取得 if( gethtml( hmtlname )~=1 )then toast( hmtlname.."に接続できませんでした") return end toast( "Connect Success!" ) --アメダスのデータを取得します Amedas = getAmedasTable( hmtlname ) --取得データを一覧する canvas.drawCls( color(255,255,255) ) --画面クリア for i=1,#Amedas do for j=1,#Amedas[i] do canvas.putText( Amedas[i][j], (j-1)*11*5, (i-1)*11, 11, color(0,0,0) ) end end canvas.putflush() touch(3) end main() system.exit()
以上です。