zoukankan      html  css  js  c++  java
  • lua工具库penlight--07函数编程(一)

    函数编程

     

    序列

    Lua 迭代器 (最简单的形式) 是一个函数,可以多次调用返回一个或多个值。for in语句理解迭代器和循环,直到该函数将返回nil。 Lua有标准的序列迭代器 (ipairspairs) , io.lines是返回文件中的所有行的迭代器。在的Penlight库中,这种迭代器也称为序列。单个值 (比如从io.lines ) 的序列称为单值,由pairs定义的序列是双值.

    pl.seq提供一些有用的迭代器和一些操作序列的函数。乍一看本示例尝试在Lua写里 Python (有序列):

     > for i in seq.range(1,4) do print(i) end

     1

     2

     3

     4

     

    但是range 实际上相当于 Python xrange,因为它会生成一个序列,不是列表。若要获取列表,请使用seq.copy(seq.range(1,10)),它使用任何单值序列作参数,并从seq.list生成表,seq.copy的行为和ipairs一样,除了它不给你索引中,只值。

     > for x in seq.list {1,2,3} do print(x) end

     1

     2

     3

     

    enum 可以把序列变成双值序列组,所以enum(list(ls))是实际上相当于ipairs 。一个更有趣的例子打印出带有行号的文件:

     for i,v in seq.enum(io.lines(fname)) do print(i..' '..v) end

     

    序列可以合并,由 'zipping(压缩)它们或将它们连接起来。

     > for x,y in seq.zip(l1,l2) do print(x,y) end

     10      1

     20      2

     30      3

     > for x in seq.splice(l1,l2) do print(x) end

     10

     20

     30

     1

     2

     3

     

    seq.printall用于打印出单值的序列,并提供一些格式化的精细控制,如分隔符,行,可以使用格式化字符串(详见string.format) 

     > seq.printall(seq.random(10))

     0.0012512588885159 0.56358531449324 0.19330423902097 ....

     > seq.printall(seq.random(10), ',', 4, '%4.2f')

     0.17,0.86,0.71,0.51

     0.30,0.01,0.09,0.36

     0.15,0.17,

     

    map 可以把在序列上应用函数

     > seq.printall(seq.map(string.upper, {'one','two'}))

     ONE TWO

     > seq.printall(seq.map('+', {10,20,30}, 1))

     11 21 31

     

    filter 可以使用布尔值的函数 (通常称为谓词)来筛选。例如,此代码仅打印文件中的全是数字的行

     for l in seq.filter(io.lines(file), stringx.isdigit) do print(l) end

     

    下面的示例返回所有大于0表 (相当于tablex.filter(ls, ‘>’, 0))

     ls = seq.copy(seq.filter(ls, '>', 0))

     

    在讨论 input.numbers时已经提到过seq.sum。这也可以seq.reduce :

     > seq.reduce(function(x,y) return x + y end, seq.list{1,2,3,4})

     10

     

    seq.reduce可以递归的调用二元函数(译注:即带有两个参数的函数)

     reduce(op,{1,2,3}) => op(1,reduce(op,{2,3}) => op(1,op(2,3))

    (译注:即不断的向op传入两个参数,直到最后两个)

    现在可以轻松地进行累积操作pl.operator提供的标准操作很有用:

     > ops = require 'pl.operator'

     > -- can also say '*' instead of ops.mul

     > = seq.reduce(ops.mul,input.numbers '1 2 3 4')

     24

     

    有一些函数可以从一个数字序列中提取统计数字:

     > l1 = List {10,20,30}

     > l2 = List {1,2,3}

     > = seq.minmax(l1)

     10      30

     > = seq.sum(l1)

     60      3

     

    会经常碰到在值重复的序列里提取值。count_map可以对这种序列进行处理,返回每个键,及其关联的值是它们出现的次数所组成的表

     > t = seq.count_map {'one','fred','two','one','two','two'}

     > = t

     {one=2,fred=1,two=3}

     

    它也可以处理数值序列,但你不能期望结果是适当的列表,即有没有hole。相反,您总是需要使用pairs来迭代结果 — — 请注意索引 处有一hole

     > t = seq.count_map {1,2,4,2,2,3,4,2,6}

     > for k,v in pairs(t) do print(k,v) end

     1       1

     2       4

     3       1

     4       2

     6       1

     

    unique使用count_map 返回唯一值得列表。

    last 可以使用当前值和前一个值把单值序列转为双值序列

     > for current,last in seq.last {10,20,30,40} do print (current,last) end

     20      10

     30      20

     40      30

     

    这样就可以轻松做事,如识别文件中重复的行,或构建值之间的差异。filter 可以处理双值的序列,通过使用operator.lt,或只是 ' <',返回当前值小于上一个值的序列,结果会复制到表中。

     > ls = {10,9,10,3}

     > = seq.copy(seq.filter(seq.last(s),'<'))

     {9,3}

     

     

    序列的包装

    pl.seq 中的函数涵盖处理序列常见模式,但将这些函数链接在一起可以导致丑陋的代码。考虑前面的例子 seq重复三次,所生成的表达式要从右到左。第一个问题可以使用局部变量别名解决,这样表达式改为copy(filter(last(s),‘<’)) 第二个问题是指秩序有点自然的应用程序。我们往往更喜欢从左到右进行读操作,这为什么面向对象的表示法成为最受欢迎的原因之一。序列适配器允许此表达式就像这样:

     seq(s):last():filter('<'):copy()

     

    这种表示法,可以用到左到右的链式调用

    序列不是一个基本的 Lua 类型,他们通常是函数或可调用对象。表达式seq(s)将一个序列包装在sequence wrapper是一个对象,可以理解pl.seq中所有的函数。然后,此对象显式表示序列。

    作为一种特殊情况,该构造函数 (这是当您调用表seq ) 将包装简单表。在这里我们将长度运算符应用到序列中的字符串,并将它们打印出来。

     > seq{'one','tw','t'} :map '#' :printall()

     3 2 1

     

    为方便,seq.lines表现就像io.lines一样,除了它包装的结果作为显式序列类型。这个例子需要从标准输入一次读入10 ,把读入的字符变为大写,并把它变成一个计数、值序列,再用连接运算符连接最后打印出带有换行符结果

     seq.lines():take(10):upper():enum():map('..'):printall ' '

     

    请注意方法upper,而不是一个seq函数。如果未知的方法被调用,序列的包装会将该方法应用到 (这是隐式使用的mapmethod 序列中的所有值 

    用这种方式创建自定义的序列很简单。在 Unix 上, /dev/random给你无限序列的随机字节,所以我们使用take 限制序列,然后map 缩放果到所需范围。关键的一步是使用seq来包装的迭代器函数:

     -- random.lua

     local seq = require 'pl.seq'

     

     function dev_random()

         local f = io.open('/dev/random')

         local byte = string.byte

         return seq(function()

             -- read two bytes into a string and convert into a 16-bit number

             local s = f:read(2)

             return byte(s,1) + 256*byte(s,2)

         end)

     end

     

     -- print 10 random numbers from 0 to 1 !

     dev_random():take(10):map('%',100):map('/',100):printall ','

     

    另一个一行Linux 程序取数据/proc文件系统,可以当前正在运行的进程的列表:

     pids = seq(lfs.dir '/proc'):filter(stringx.isdigit):map(tonumber):copy()

     

    此版本的Penlight有一个实验性的功能它依赖于所有Lua 类型可以都有 metatables,包括函数的事实。这使隐式序列包装成为可能:

     > seq.import()

     > seq.random(5):printall(',',5,'%4.1f')

      0.0, 0.1, 0.4, 0.1, 0.2

     

    可以避免seq(seq.random(5))尴尬的构造,完全来自其他地方迭代器,如下:

     > ('one two three'):gfind('%a+'):printall(',')

     one,two,three,

     

    seq.import后不再需要显式包装序列函数。

    但对于这种便利付出的代价。每个函数都受影响,这样的任何函数可以用,适当的或不适当的

     > math.sin:printall()

     ..seq.lua:287: bad argument #1 to '(for generator)' (number expected, got nil)

     > a = tostring

     > = a:find(' ')

     function: 0042C920

     

    什么函数被返回了?它是几乎肯定会在当前上下文中没有任何意义的东西。所以隐式序列可能会使某些类型的编程错误的更难抓到 — — 他们是最用于交互式探索和小脚本。

  • 相关阅读:
    C#深入浅出 修饰符(二)
    HDU 5785 Interesting
    HDU 5783 Divide the Sequence
    HDU 5781 ATM Mechine
    UVA 714 Copying Books
    uva 1471 Defense Lines
    UVA 11134 Fabled Rooks
    UVA 11572 Unique Snowflakes
    UVA 11093 Just Finish it up
    UVA 10954 Add All
  • 原文地址:https://www.cnblogs.com/xdao/p/lua_penlight07_1.html
Copyright © 2011-2022 走看看