zoukankan      html  css  js  c++  java
  • 对lua协程的一点理解

    读《Programming In Lua》协程那一章,比较困惑的还是procuer-consumer那个例子:

    function consumer(prod)
        while true do
            local x = receive(prod)
            print(x)
        end
    end
    
    function receive(prod)
        local status, value = coroutine.resume(prod)
        return value
    end
    
    function send(x)
        coroutine.yield(x) -- go back to where resumed
    end
    
    function producer()
        return coroutine.create(function()
            while true do
                local x = io.read()
                send(x)
            end
        end)
    end
    
    -- consumer-driven design
    consumer(producer())

    producer产生数据,consumer消费数据,producer与consumer都在各自的协程中完成, 代码很短,但是很难读 - 至少不是那么一目了然,尤其比起这个直接的循环:

    function produce()
        return io.read()
    end
    
    function consume(x)
        print(x)
    end
    
    while true do
        local x = produce()
        consume(x)
    end

    好在哪里?

    书中说可以添加缓存控制速度,或者进行数据过滤 - 但是这在循环版本的producer-consumer中也都能做到,无非在在实现produce是多些逻辑,或者再加个filter的函数处理produce的返回值,协程版本毫无优势可言。

    实在要说其优点,恐怕只是:producer和consumer的代码在各自的协程中实现,并通过resume-yield交换数据 - 实现了松耦合。这是个优点,可以还是不太满意,再往下看时,看到了全排列那个例子,稍作修改,让我比较直观的感觉到了协程这种控制结构的灵活性:

    function send(a)
        coroutine.yield(a)
    end
    
    function receive(prod)
        local status, value = coroutine.resume(prod)
        return value
    end
    
    function consumer(prod)
        local function print_result(a)
            for _, v in ipairs(a) do
                io.write(v, " ")
            end
            io.write('\n')
        end
        
        while true do
            local a = receive(prod)
            if not a then break end
            print_result(a)
        end
    end
    
    
    function producer(a)
        function permgen(a, n)
            if n == 0 then
                send(a) -- send something for consuming from an recursive call
            else
                for i=1, n do
                    a[n], a[i] = a[i], a[n]
                    permgen(a, n-1)
                    a[n], a[i] = a[i], a[n]
                end
            end
        end
    
        local n = table.getn(a)
        return coroutine.create(function() permgen(a, n) end)
    end
    
    consumer(producer({1,2,3,4}))

    这里全排列采用了递归算法:对数组中的每个元素,把它交换到数组末尾,然后对前n-1个元素的子数组做同样的事,当n==0时,输出一个排列。

    这个在produce的时候,因为在一个递归调用中,你无法一个个返回:

    • 要么每找到一个,直接在里面处理 - 这样produce-consume的结构就有点混淆在一起;
    • 或者先把结果保存在一个共享内存中,产生全部排列后,再逐个处理 - 这样要另外开辟空间,流程上感觉也不够简洁。

    于是,协程的有点就显现出来了,不同于简单函数中的return,协程可以在找到一个排列后,yield挂起本协程并返回该排列,返回到原来resume这个协程的代码处,取得数据进行consume,然后继续resume进入协程获取排列 - 通过协程灵活控制流程并传递数据,十分漂亮。

    所以,那个循环版本的问题是:并不是所有produce的数据,都可以简单的return回来的。

  • 相关阅读:
    MSSQL大量数据时,建立索引或添加字段后保存更改超时该这么办
    POJ 3261 Milk Patterns (后缀数组)
    POJ 1743 Musical Theme (后缀数组)
    HDU 1496 Equations (HASH)
    694. Distinct Substrings (后缀数组)
    POJ 1222 EXTENDED LIGHTS OUT (枚举 或者 高斯消元)
    POJ 1681· Painter's Problem (位压缩 或 高斯消元)
    POJ 1054 The Troublesome Frog (hash散列)
    HDU 1716 排列2
    HDU 4405 Aeroplane chess (概率DP & 期望)
  • 原文地址:https://www.cnblogs.com/baiyanhuang/p/2775315.html
Copyright © 2011-2022 走看看