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

     

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

  • 相关阅读:
    报错:No module named 'ConfigParser'
    报错:AttributeError: module 'selenium.webdriver' has no attribute 'Chrome'
    报错:selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element:
    去掉“Chrome正在受到自动化测试软件的控制”
    XPath-Helper 的安装和使用
    Chrome您使用的是不受支持的命令行标记:--ignore-certificate-errors
    Message: unknown error: cannot get automation extension
    安装mysql-5.7.22-winx64中出现错误
    pip install 报错 Could not fetch URL
    Spring Boot 报错:Error creating bean with name 'entityManagerFactory' defined in class path resource
  • 原文地址:https://www.cnblogs.com/xdao/p/lua_penlight07_1.html
Copyright © 2011-2022 走看看