Welcome Guest ( Log In | Register )

欢迎访问本站。游客仅能浏览首页新闻、版块主题、维基条目与资源信息,需登录后方可获得内容发布、话题讨论、维基编辑与资源下载等权限。若无账号请先完成注册流程。
 
Reply to this topicStart new topic
> Lua进阶:函数
bx_bob
2011-08-03, 17:27
Post #1


比非主流还非主流的Lua原教旨主义机械师
Group Icon
 817
   15

Group: Avatar
Posts: 416
Joined: 2007-07-25
Member No.: 14794


有时候,写代码和法师撰写卷轴的意思差不多:除了运行就要有效果,还要让别人能够阅读和抄写。(DND卷轴啦)
一份一眼就能判断出作用的代码,不止可以节省其他人阅读的时间,也有助于自己在将来的复用和维护。
所以相比起好好写代码的效益而言,付出的代价要小得多。
» Click to show Spoiler - click again to hide... «
代码的风格可以说是因人而异的,但是好的代码总有一些共通之处:
  1. 清晰完整且不过分的注释
  2. 统一且简单的命名方式
  3. 整齐的缩进与换行
以上是一些唠叨。接下来要提到的是:如何更加“优雅”地撰写代码?
答案是:优良的封装。

这里是我早前编写的一个示例,它的特点是:几乎没有封装。
由于当中也没有什么需要循环的部分,所以也没有什么缩进。
这样的代码风格有时候被人叫做临时脚本,但是我们只需要对它稍加修改,就能变成一个可以在以后随时复制粘贴的“模块”。
» Click to show Spoiler - click again to hide... «

1.分析
这个脚本的功能是从trow论坛抓取一些东西,分析之后输出到excel文件并且生成一个图表。
只从复用的价值来说,从网络(不限于trow.cc)抓取内容以后会常用到。分析html结果意义不大,因为判断是随着内容不同而需要改变的。输出到excel文件也不能算十分有用,因为数据量与类型都不一定能再遇上,但是新建一个Excel文件还是有点作用的。
于是我们可以确定:最需要封装的是网络部分,其次是新建Excel文件,其他的随意封装一下好了。
CODE
html_result=""
fakebrowser=curl.new()
fakebrowser:setopt(curl.OPT_URL,"http://trow.cc/forum/index.php?act=idx")
fakebrowser:setopt(curl.OPT_WRITEFUNCTION,function(i,buffer) html_result=html_result..buffer return #buffer end)
fakebrowser:perform()
fakebrowser:close()
根据之前写好的注释,这一段就是网络访问部分,我们稍加修改一下
CODE
function get_html(url)  --get html from url, require luacurl
  local html_result=""
  local fakebrowser=curl.new()
  fakebrowser:setopt(curl.OPT_URL,url)
  fakebrowser:setopt(curl.OPT_WRITEFUNCTION,function(i,buffer) html_result=html_result..buffer return #buffer end)
  fakebrowser:perform()
  fakebrowser:close()
  fakebrowser=nil
  return html_result
end
切记变量的可视原则,把函数内部的变量前面加上local,免得污染全局变量环境。
但是这样的话,后面的html_result变量就没有用了,要解决很容易,加上一句:
CODE
local html_result=get_html("http://trow.cc/forum/index.php?act=idx")
接下来是处理数据部分,随便一塞好了。原文就不赘述了。
CODE
function proc_html(html)
    --从数据中抽取需要的内容:游客数量,注册用户数量,隐身用户数量
    local guests_num,members_num,anony_num=string.match(html,"\n.*<b>(.-)</b> guests, <b>(.-)</b> members <b>(.-)</b> anonymous members")
    print(guests_num,members_num,anony_num)
    return guests_num,members_num,anony_num
end
(IMG:style_emoticons/default/excl.gif) lua函数可以返回多个变量,请参考本文后部
嗯,这里同样也得加上
CODE
local guests_num,members_num,anony_num=proc_html(html_result)
哦,在这里不得不顺带一提的是,魔囧更喜欢把这里写成:
CODE
local guests_num,members_num,anony_num=proc_html(get_html("http://trow.cc/forum/index.php?act=idx"))
这等于把前两个改动集成到一句话里面了,这个风格叫...函数式编程。

最终的代码长这么一副样子:
CODE
--excel and trow
module("trow2excel",package.seeall)
require("luacurl")
require("luacom")
--从trow论坛页面获取数据

function get_html(url)  --get html from url, require luacurl
    local html_result=""
    local fakebrowser=curl.new()
    fakebrowser:setopt(curl.OPT_URL,url)
    --代理
    fakebrowser:setopt(curl.OPT_PROXY,"proxy.chn.xerox.com")
    fakebrowser:setopt(curl.OPT_PROXYPORT,8000)
    fakebrowser:setopt(curl.OPT_WRITEFUNCTION,function(i,buffer) html_result=html_result..buffer return #buffer end)
    fakebrowser:perform()
    fakebrowser:close()
    fakebrowser=nil
  return html_result
end

local function proc_html(html)
    --从数据中抽取需要的内容:游客数量,注册用户数量,隐身用户数量
    local guests_num,members_num,anony_num=string.match(html,"\n.*<b>(.-)</b> guests, <b>(.-)</b> members <b>(.-)</b> anonymous members")
    print(guests_num,members_num,anony_num)
    return guests_num,members_num,anony_num
end

function call_excel()
    --创建一个Excel OLE对象
    local excel = luacom.CreateObject("Excel.Application")
    assert(excel)
    --对Excel OLE各种操作,加入数据并创建一个饼图
    excel.Visible=true
    local book=excel.Workbooks:Add()
    local sheet=book.Worksheets(1)
    return excel,book,sheet
end

local function excel_draw(excel,sheet,chart_type,range)
    local chart=excel.Charts:Add()
    chart.ChartType=chart_type --饼图
    local range=sheet:Range(range)
    chart:SetSourceData(range)
end

local function write_excel(sheet,guests_num,members_num,anony_num)
    sheet.Cells(1,1).Value2="游客"
    sheet.Cells(1,2).Value2="注册用户"
    sheet.Cells(1,3).Value2="隐身用户"
    sheet.Cells(2,1).Value2=guests_num
    sheet.Cells(2,2).Value2=members_num
    sheet.Cells(2,3).Value2=anony_num
end

local html_result=get_html("http://trow.cc/forum/index.php?act=idx")
local guests_num,members_num,anony_num=proc_html(html_result)
local excel,book,sheet=call_excel()
write_excel(sheet,guests_num,members_num,anony_num)
excel_draw(excel,sheet,5,"A1:C2")

实际上这里显然是过度封装了,而且write_excel和excel_draw的封装还非常混乱(幸好以后也不怎么用他们?)
这里的头部加上了Lua的模块管理,把这个模块命名做"trow2excel"(但是实际上暴露出来的只有两个函数:网络访问与创建excel实例。这两个函数的功能其实没有太大关系,所以放在一个模块里不合适,效率也没有优化,不过作为教程就先这么凑合吧)

以后想要复用这个里面的代码,可以把最后这几行实际的调用删掉。
在新代码里只需要require("trow2excel"),然后就能通过trow2excel.get_html("xxxxx")来调用获取html的函数了。

-------------
可变长参数
-------------
CODE
function test(...)
  t={...}
  print(t[2])
end

test(1,2,3)

-------------
多个返回值
-------------
lua的赋值有个特性,可以让你忘掉c语言中入门的那个交换函数。
CODE
a,b=b,a
(IMG:style_emoticons/default/excl.gif)
» Click to show Spoiler - click again to hide... «
嗯,多个返回值的函数可以这样写
CODE
function test()
return 1,2
end
a,b=test()
print(a,b)
结果就是
QUOTE
1 2

This post has been edited by bx_bob: 2011-08-10, 17:02
TOP
jade_phenix
2011-08-05, 09:37
Post #2


主物质者
Group Icon
 -34
   0

Group: Primer
Posts: 52
Joined: 2009-04-08
Member No.: 29596


WOW……可以返回多个值呢……
TOP
bx_bob
2011-08-10, 17:02
Post #3


比非主流还非主流的Lua原教旨主义机械师
Group Icon
 817
   15

Group: Avatar
Posts: 416
Joined: 2007-07-25
Member No.: 14794


更新了好叉大一段,所以自己顶一下
TOP
Fast ReplyReply to this topicStart new topic
 


Time is now: 2021-10-27, 13:49