Preface
Goal: Continue Part One.
5: More Functional
However, I am still curious about using fun
library.
Flatten Module
Of course flatten can be done using foldl
.
But the code looks long, so I’d better put in separate library.
function extract(songs)
return map(
function (song) return song.tags end, songs)
end
function clean(tags)
return filter(
function (tag) return tag end, tags)
end
function flatten_rec(init, tags)
return foldl(
function(acc, tag)
if type(tag) == 'string' then
acc[#acc + 1] = tag
return acc
end
if type(tag) == 'table' then
return flatten_rec(acc, tag)
end
end,
init, tags
)
end
function flatten(tags)
return flatten_rec({}, tags)
end
I do not feel pleased with the length of the code. But here we are. I have to make peace with Lua limitation.
Using Flatten Module
Now we can have a very short code.
local inspect = require('inspect')
require 'fun' ()
require 'my-songs'
require 'my-flatten'
print(inspect( flatten( clean(extract(songs)) ) ))
With the result similar as below:
$ lua 09-flatten.lua
{ "60s", "jazz", "60s", "rock", "70s", "rock", "70s", "pop" }
Of course with hidden implementation, in module.
Exclude Algorithm
To get a unique
array, we need to create exclude
function first.
This come in my mind, inspired by haskell algorithm.
exclude :: String -> ([String] -> [String])
exclude tag = filter((/=) tag)
unique :: [String] -> [String]
unique [] = []
unique (tag:tags) = tag:unique(exclude tag tags)
tags :: [String]
tags = ["rock", "jazz", "rock", "pop", "pop"]
main = print (unique tags)
Exclude in Lua
We can just implement the filter using fun
filter library,
and get about similar result with Haskell.
local inspect = require('inspect')
require 'fun' ()
require 'my-songs'
require 'my-flatten'
function print_inspect(tags)
print(inspect(tags))
end
function exclude(val, tags)
return filter(
function (tag) return tag ~= val end, tags)
end
local tags = flatten( clean(extract(songs)) )
each(print_inspect, songs)
The result is as below:
$ lua 10-exclude.lua
"60s"
"jazz"
"60s"
"rock"
"70s"
"rock"
"70s"
"pop"
Generator Issue
We can try to change a bit, to make the reesult oneliner
print_inspect( exclude("rocks", tags) )
But the result is a loooong code:
$ lua 10-exclude.lua
{
gen = <function 1>,
param = { <function 2>, <function 3>, { "60s", "jazz", "60s", "rock", "70s", "rock", "70s", "pop" } },
state = 0,
<metatable> = {
…
We can have a look at the type with this line:
print( exclude("rocks", tags) )
Now we know the problem. This is no longer simple array, but generator.
$ lua 10-exclude.lua
<generator> table: 0x5640f3c62870 0
That’s it.
Tools Module
We can fix this by using custom normalize function. For convenience, I put this in separate module.
local inspect = require('inspect')
function normalize(tags)
if type(tags) == 'table' then
local acc = {}
function add_array(new)
acc[#acc + 1] = new
end
each(add_array, tags)
return acc
end
return tags
end
function print_inspect(tags)
print(inspect(normalize(tags)))
end
Each inspect
time, I normalize the array first.
Merge Table
I also add merge table capability.
function join_tables(t1, t2)
for k,v in ipairs(t2) do table.insert(t1, v) end return t1
end
Seem like Lua
lack of tools.
But it is the a lightweight language is.
Unique (Distinct)
Now we can peacefully, porting algorithm, from previously in Haskell to Lua.
local inspect = require('inspect')
require 'fun' ()
require 'my-songs'
require 'my-flatten'
require 'my-tools'
function exclude(val, tags)
return normalize(filter(
function (tag) return tag ~= val end, tags))
end
function unique(tags)
if type(tags) == 'table' then
if #tags == 0 then return {} end
if #tags == 1 then return { head(tags) } end
return join_tables(
{ head(tags) },
unique( exclude(head(tags), tail(tags)) )
)
end
end
local tags = flatten( clean(extract(songs)) )
print_inspect( unique(tags) )
With the result similar as below:
❯ lua 11-unique.lua
{ "60s", "jazz", "rock", "70s", "pop" }
Despite the mess I wrote above. It has been very interesting to work with Lua. I’m looking forward to explore more Lua.
6: Concurrency with Coroutine
Yes, Lua is capable of concurrency. But not in parallelism context.
My code below is based on the official documentation
from the Programming in Lua
first edition book.
We can, rewrite our previous flatten code with actor pattern,
using sender
and receiver
.
Reference Reading
Prepare
First thing first, the dependency.
local inspect = require('inspect')
require 'my-songs'
Sender
Consider rewrite the sender from example above to suit our custom song task.
function sender ()
return coroutine.create(function ()
for _, song in pairs(songs)
do
if type(song.tags) == 'table' then
for _, tag in pairs(song.tags)
-- send messages
do coroutine.yield(tag) end
end
end
end)
end
_.
Receiver
And also onsider rewrite the receiver from example above to suit our custom song task.
function receiver (prod)
local tags = {}
while true do
-- receive message
local status, message = coroutine.resume(prod)
if message==nil
then break
else tags[#tags + 1] = message
end
end
return tags
end
Running Both
Pretty short right!
Consider gather both function.
prod = sender()
tags = receiver(prod)
print(inspect(tags))
With the result similar as below array
:
$ lua 12-coroutine.lua
{ "60s", "jazz", "60s", "rock", "70s", "rock", "70s", "pop" }
That is all.
Real Life Lua
You might want to have a look of what Lua can do. I have made an article series about AwesomeWM configuration, written in Lua.
What is Next 🤔?
Consider continue reading [ Julia - Playing with Records - Part One ].