• lua工具库penlight--07函数编程(二)


    列表压缩

    列表压缩是以紧凑的方式通过指定的元素创建表。在 Python,你可以说:

     ls = [x for x in range(5)]  # == [0,1,2,3,4]

     

    在 Lua,使用pl.comprehension :

     > C = require('pl.comprehension').new()

     > = C ('x for x=1,10') ()

     {1,2,3,4,5,6,7,8,9,10}

     

    C是一个函数,它将一个列表字符串编译成一个函数。在这种情况下,该函数有没有参数。考虑一个字符串作为函数参数括号是冗余因此可以

     > = C 'x^2 for x=1,4' ()

     {1,4,9,16}

     > = C '{x,x^2} for x=1,4' ()

     {{1,1},{2,4},{3,9},{4,16}}

     

    注意该表达式可以是任何变量x的函数!

    基本的语法到目前为止是 for ,可以是任何 Lua for理解语句可以只是变量,在这种情况下的值将来自该参数的“语句”。在这里我强调,“语句”是可以带列表参数的函数:

     > = C '2*x for x' {1,2,3}

     > = C '2*x for x' {1,2,3}

     {2,4,6}

     > dbl = C '2*x for x'

     > = dbl {10,20,30}

     {20,40,60}

     

    这里是稍微更加明确说明,1是的占位符参数,指传递给“语句”的第一个参数。

     > = C '2*x for _,x in pairs(_1)' {10,20,30}

     {20,40,60}

     > = C '_1(x) for x'(tostring,{1,2,3,4})

     {'1','2','3','4'}

     

    当您想要一些收集结果的迭代器,如io.lines,此扩展的语法非常有用。这里“语句”创建一个函数,文件中所有行创建一个表:

     > f = io.open('array.lua')

     > lines = C 'line for line in _1:lines()' (f)

     > = #lines

     118

     

    许多函数都可以应用到“语句”的结果:

     > = C 'min(x for x)' {1,44,0}

     0

     > = C 'max(x for x)' {1,44,0}

     44

     > = C 'sum(x for x)' {1,44,0}

     45

     

    (这些是相当于减少操作

    可能for的后面有条件,用来筛选输出。下面这条“语句”收集偶数列表:

     > = C 'x for x if x % 2 == 0' {1,2,3,4,5}

     {2,4}

     

    可能有多个for部分

     > = C '{x,y} for x = 1,2 for y = 1,2' ()

     {{1,1},{1,2},{2,1},{2,2}}

     > = C '{x,y} for x for y' ({1,2},{10,20})

     {{1,10},{1,20},{2,10},{2,20}}

     

    这些“语句”很有用,特别是处理多个变量时,如果用其他的Penlight函数,不是那么轻易达到目的

     

     

    从函数创建函数

    Lua 函数像任何其他值,当然你不能或添加它们(原文,of course you cannot multiply or add them.。有意义操作是函数组成compose函数调用链 ((f * g)(x)f(g(x)).)

     > func = require 'pl.func'

     > printf = func.compose(io.write,string.format)

     > printf("hello %s ",'world')

     hello world

     true

     

    许多功能需要您将一个函数作为参数传递,应用于所有值的序列或作为一个回调。经常有用的函数具有错误的参数数目。所以有必要将传给函数的两个参数变为一个参数,一个绑定到一个给定值的额外参数。

    部分应用的 参数的函数并返回 n-1 参数,它的第一个参数绑定到一些值:

     > p2 = func.bind1(print,'start>')

     > p2('hello',2)

     start>  hello   2

     > ops = require 'pl.operator'

     > = tablex.filter({1,-2,10,-1,2},bind1(ops.gt,0))

     {-2,-1}

     > tablex.filter({1,-2,10,-1,2},bind1(ops.le,0))

     {1,10,2}

    (译注:即有些函数是两个参数,但是调用者只能传递一个参数,于是需要一个bind1,绑定一个预定的值,这样传入的一个参数与这个预定值比较。

    不幸的是最后一个例子结果相反,因为bind1总是绑定的第一个参数 !(译注:0<x,结果都是比0大的)。此外不幸的是,之前我困惑 '扩充与 '部分应用',所以bind1的旧名称是curry— — 此别名仍然存在。

     

    这是一种特殊的形式的函数参数绑定。这里是另一种方法说的打印示例:

     > p2 = func.bind(print,'start>',func._1,func._2)

     > p2('hello',2)

     start>  hello   2

     

    这里和 2是占位符变量,分别对应于第一和第二个参数。

    func 会让人分心,所以可以 pl.func拉到本地上下文中。这里是筛选器的例子:

     > utils.import 'pl.func'

     > tablex.filter({1,-2,10,-1,2},bind(ops.gt, _1, 0))

     {1,10,2}

    (译注:即不用写func

    tablex.merge可以合并两个表。此示例演示绑定函数最后一个参数的用处。

     > S1 = {john=27, jane=31, mary=24}

     > S2 = {jane=31, jones=50}

     > intersection = bind(tablex.merge, _1, _2, false)

     > union = bind(tablex.merge, _1, _2, true)

     > = intersection(S1,S2)

     {jane=31}

     > = union(S1,S2)

     {mary=24,jane=31,john=27,jones=50}

     

    当用bind print绑定时,我们得到有两个参数的函数而我们真正想要的是使用 varargs ,就像print一样(译注:可变参数)。这是_0的作用:

     > _DEBUG = true

     > p = bind(print,'start>', _0)

     return function (fn,_v1)

         return function(...) return fn(_v1,...) end

     end

    (译注:上面的是bind后输出的结果,不是手工输入的

     > p(1,2,3,4,5)

     start>  1       2       3       4       5

     

    我已经开启全DEBUG标志,以便打印生成的函数来。它是实际的函数, 第一次调用绑定值的 v1到 start >'

     

    占位符表达式

    Penlight中的常见模式是把函数应用于一个表或序列的所有元素,如tablex.mapseq.filterLua 有匿名函数,虽然他们可能有点乏味:

     > = tablex.map(function(x) return x*x end, {1,2,3,4})

     {1,4,9,16}

     

    pl.func允许您定义的占位符表达式,可以砍倒烦恼的打字,并且还使您的意图更明确。首先,我们把pl.func引入到我们的上下文,提供表达式中使用占位符变量,例如12,等等 (c + + 程序员将会认识到这从 Boost 借鉴)

     > utils.import 'pl.func'

     > = tablex.map(_1*_1, {1,2,3,4})

     {1,4,9,16}

     

    可以生成的最多 个参数的函数。

     > = tablex.map2(_1+_2,{1,2,3}, {10,20,30})

     {11,22,33}

     

    这些表达式可以使用任意函数,他们必须首先注册到函数编程库func.register引入单个函数,func.import导入整个表的功能,如math.

     > sin = register(math.sin)

     > = tablex.map(sin(_1), {1,2,3,4})

     {0.8414709848079,0.90929742682568,0.14112000805987,-0.75680249530793}

     > import 'math'

     > = tablex.map(cos(2*_1),{1,2,3,4})

     {-0.41614683654714,-0.65364362086361,0.96017028665037,-0.14550003380861}

     

    常见的操作对调用一组对象的方法:

     > = tablex.map(_1:sub(1,1), {'one','four','x'})

     {'o','f','x'}

     

    PEs 操作有一些限制。例如, __len 元方法不能被普通的 Lua 表重写,因为我们需要定义一个特殊的函数来表达#_1':

     > = tablex.map(Len(_1), {'one','four','x'})

     {3,4,1}

     

    同样对于比较运算符,不能比较不同的类型,必须表示为一个特殊的函数

     > = tablex.filter(Gt(_1,0), {1,-1,2,4,-3})

     {1,2,4}

     

    函数返回多个值非常有用。例如, tablex.pairmap期望函数返回键和值,并返回新值和键

     > = pairmap(Args(_2,_1:upper()),{fred=1,alice=2})

     {ALICE=2,FRED=1}

     

    PEs 不能包含nil值,因为 PE 函数参数表示为一个数组。相反,提供一个特别的值称为Nil。所以请用1:f(Nil,1) 而不是 1:f(nil,1).

     

    占位符表达式,不能自动作为 Lua 函数的使用必须将调用运算符重载构造函数调用_1(1)。如果您想要强制一个 PE 要返回一个函数,请使用 func.I .

     > = tablex.map(_1(10),{I(2*_1),I(_1*_1),I(_1+2)})

     {20,100,12}

     

    在这里我们考虑含有单个参数函数的表,然后10调用它们。

    用 PEs 的基本理念是用引号界定一个表达式,因此它不会立即进行计算,而是变成了一个以后可以应用到的某些参数的函数。基本机制是包裹值和占位符这样普通的Lua 运算符有建立一个表达式树的效果。(这样你可以使用 PEs做符号代数,请参阅symbols.lua中的示例testsym.lua演示了象征性分化的测试)

     

    规则是如果任何运算符有 PE 操作数结果会被引号引起。有时我们需要显式引用的东西。例如,如果该元素值是set里的,我们想传递一个必须返回 true的函数给筛选器。set[1]是的明显的表达式,但是它没有返回所需的结果,因为它直接计算结果,得到了nil索引比二元运算相比,如加法 set +1适当地使用引号),有些不同。所以有显式引用或包装操作的需求。这就是function干的事情, 在这种情况下PE应该是 (set)[1]。这适用于函数,方便的替代办法注册函数: (math.sin)(_1)。这就相当于使用lines' 方法:

     for line in I(_(f):read()) do print(line) end

     

    '文件的对象都可以工作只要有返回下一行的read方法。如果你有一个 LuaSocket 客户,被服务器逐行‘推’那么_(s):receive ‘*l’将会创建一个迭代器用于接收输入。这些形式可以方便的调整您的数据流量,使它可以传递到pl.seq'的函数.

     

    占位符表达式可以序列的包装表达式混合。lexer.lua 将给我们双值标记,其中的第一个是类型,第二个值。我们筛选出类型 'iden'使用map提取实际值 获取唯一值最后将结果存list中。

     > str = 'for i=1,10 do for j = 1,10 do print(i,j) end end'

     > = seq(lexer.lua(str)):filter('==','iden'):map(_2):unique():copy()

     {i,print,j}

     

    看起来特别密集(我总是觉得不应该一行程序解决一切!) ;关键是map行为,它使用序列两个值,因此_2返回的值部分。(因为filter在这里使用额外的参数,它只操作类型值。)

    使用占位符表达式有一些性能需求要考虑到。实例化一个 PE 需要构建和编译一个函数,这不是这种快速的操作。所以要获得最佳性能,从循环分解出 PEs  

     local fn = I(_1:f() + _2:g())

     for i = 1,n do

         res[i] = tablex.map2(fn,first[i],second[i])

     end

     

  • 相关阅读:
    解决org.openqa.selenium.WebDriverException: Unable to connect to host 127.0.0.1 on port 7055 after 45000 ms org.springframework.beans.BeanInstantiation
    jsp学习---css基础知识学习,float,position,padding,div,margin
    jsp学习---mvc模式介绍和el表达式,jstl标签库的使用入门
    java 查询 mongodb 中的objectid
    jsp学习---使用jsp和JavaBean实现超简单网页计算器
    jsp学习--JavaBean定义和在Jsp中使用JavaBean
    jsp学习--如何定位错误和JSP和Servlet的比较
    jsp学习--JSP运行原理,九大隐式对象和JSP常用标签
    jsp学习--基本语法和基础知识
    android开发学习---layout布局、显示单位和如何进行单元测试
  • 原文地址:https://www.cnblogs.com/xdao/p/lua_penlight07_2.html
Copyright © 2020-2023  润新知