How to initialize list using macros and then generate same set of functions in 2 different modules? - metaprogramming

Is it possible to initialize list using macros and then with another macro generate 2 sets of functions in 2 different modules based on that list?
Pseudo code example:
defmodule ABC do
defmacro rules() do
quote do
defrule("a")
|> defrule("b")
|> defrule("c")
end
end
end
defmodule BasicTokenizer do
use TokenizerBuilder
require ABC
ABC.rules()
|> deftokenizer()
end
defmodule ExtendedTokenizer do
use TokenizerBuilder
require ABC
ABC.rules()
|> defrule("d")
|> deftokenizer()
end
import ExUnit.Assertions, only: [assert: 1, assert: 2]
assert BasicTokenizer.tokenize("a") == "a"
assert BasicTokenizer.tokenize("b") == "b"
assert BasicTokenizer.tokenize("c") == "c"
assert ExtendedTokenizer.tokenize("a") == "a"
assert ExtendedTokenizer.tokenize("b") == "b"
assert ExtendedTokenizer.tokenize("c") == "c"
assert ExtendedTokenizer.tokenize("d") == "d"
I tried following approach but I'm stuck in deftokenizer:
defmodule TokenizerBuilder do
defmacro __using__(_) do
quote do
require unquote(__MODULE__)
import unquote(__MODULE__)
end
end
defmacro defrule(str) do
quote do
[unquote(str)]
end
end
defmacro defrule(rules, str) do
quote do
Enum.concat(unquote(rules), [unquote(str)])
end
end
defmacro deftokenizer(rules) do
# rules is AST, how to get builded list value outside quote?
Enum.each(rules, fn(str) ->
quote do
def tokenize(unquote(str)) do
unquote(str)
end
end
end)
end
end

It's hard to tell exactly what you're asking for but consider this code:
iex(1)> f = &String.upcase/1
&String.upcase/1
iex(2)> g = &String.trim/1
&String.trim/1
iex(3)> h = &String.reverse/1
&String.reverse/1
iex(4)> fs = [f,g,h]
[&String.upcase/1, &String.trim/1, &String.reverse/1]
iex(5)> r = for f <- fs, do: f.("hello")
["HELLO", "hello", "olleh"]
I mean if you're trying to build a list of functions to apply to a value, then this may work. I think there are ways to build a list of functions to apply to a value without having to resort to macros to do so. But your use case is far from clear to me.

Related

Split String and Include Delimiter in Lua

I searched on Google and on Stack Overflow and didn't find answer for this question. Looking at the documentation I didn't find how to do this because every function that allows splits excludes the delimiter.
EDIT
for i, word in pairs(split(text, "<(.-)>")) do
print(word)
end
function split(string, delimiter) -- Got this function from https://helloacm.com/split-a-string-in-lua/
result = {};
for match in (string..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end
This code replaces the parts in the format "<(.-)>"
Example:
Input: "Hello<a>World</a>!"
Expected Output: {"Hello", "<a>", "World", "</a>", "!"}
Real Output: {"Hello", "World", "!"}
s = "Hello<a>World</a>!"
for a in s:gsub('%b<>','\0%0\0'):gmatch'%Z+' do
print(a)
end
I assume this is related to HTML tags or similar.
One quick-n-dirty possibility I can think of that should cover your specific use case is this:
s = 'Hello<a>World</a>!'
function split(s)
local ans = {}
for a,b in (s..'<>'):gmatch '(.-)(%b<>)' do
ans[#ans+1] = a
ans[#ans+1] = b
end
ans[#ans] = nil
return ans
end
for _,v in ipairs(split(s)) do
print(v)
end

Getting the largest and smallest word at a string

when I run this codes the output is (" "," "),however it should be ("I","love")!!!, and there is no errors . what should I do to fix it ??
sen="I love dogs"
function Longest_word(sen)
x=" "
maxw=" "
minw=" "
minl=1
maxl=length(sen)
p=0
for i=1:length(sen)
if(sen[i]!=" ")
x=[x[1]...,sen[i]...]
else
p=length(x)
if p<min1
minl=p
minw=x
end
if p>maxl
maxl=p
maxw=x
end
x=" "
end
end
return minw,maxw
end
As #David mentioned, another and may be better solution can be achieved by using split function:
function longest_word(sentence)
sp=split(sentence)
len=map(length,sp)
return (sp[indmin(len)],sp[indmax(len)])
end
The idea of your code is good, but there are a few mistakes.
You can see what's going wrong by debugging a bit. The easiest way to do this is with #show, which prints out the value of variables. When code doesn't work like you expect, this is the first thing to do -- just ask it what it's doing by printing everything out!
E.g. if you put
if(sen[i]!=" ")
x=[x[1]...,sen[i]...]
#show x
and run the function with
Longest_word("I love dogs")
you will see that it is not doing what you want it to do, which (I believe) is add the ith letter to the string x.
Note that the ith letter accessed like sen[i] is a character not a string.
You can try converting it to a string with
string(sen[i])
but this gives a Unicode string, not an ASCII string, in recent versions of Julia.
In fact, it would be better not to iterate over the string using
for i in 1:length(sen)
but iterate over the characters in the string (which will also work if the string is Unicode):
for c in sen
Then you can initialise the string x as
x = UTF8String("")
and update it with
x = string(x, c)
Try out some of these possibilities and see if they help.
Also, you have maxl and minl defined wrong initially -- they should be the other way round. Also, the names of the variables are not very helpful for understanding what should happen. And the strings should be initialised to empty strings, "", not a string with a space, " ".
#daycaster is correct that there seems to be a min1 that should be minl.
However, in fact there is an easier way to solve the problem, using the split function, which divides a string into words.
Let us know if you still have a problem.
Here is a working version following your idea:
function longest_word(sentence)
x = UTF8String("")
maxw = ""
minw = ""
maxl = 0 # counterintuitive! start the "wrong" way round
minl = length(sentence)
for i in 1:length(sentence) # or: for c in sentence
if sentence[i] != ' ' # or: if c != ' '
x = string(x, sentence[i]) # or: x = string(x, c)
else
p = length(x)
if p < minl
minl = p
minw = x
end
if p > maxl
maxl = p
maxw = x
end
x = ""
end
end
return minw, maxw
end
Note that this function does not work if the longest word is at the end of the string. How could you modify it for this case?

How to check for substrings

a="hello"
b="hi"
io.write("enter a or b or both:")
c=io.read()
if c=="hello" then
print(b)
elseif c=="hi" then
print (a)
elseif c~=a or b then
print ("unknown word")
end
The problem is when I write both : hello hi, it is showing :unknown word.
How can I fix this?
I also tried with table something like d={},d.a="hello",d.b="hi" but the same problem.
== is used to test equality. But the string "hello hi" is neither equal to "hello" nor "hi". To test if it contains a substring, use pattern matching:
local found = false
if c:match("hello") then
print(a)
found = true
end
if c:match("hi") then
print(b)
found = true
end
if not found then
print ("unknown word")
end
If you want to compare words instead of substrings, try this:
function normalize(x)
return " "..x.." "
end
mywords=normalize("hello hi")
function ok(x)
return mywords:match(normalize(x))~=nil
end
print(ok("hello"))
print(ok("hi"))
print(ok("high"))

String Manipulation in Lua: Make the odd char uppercase

I'm trying to do a library in Lua with some function that manipulate strings.
I want to do a function that changes the letter case to upper only on odd characters of the word.
This is an example:
Input: This LIBRARY should work with any string!
Result: ThIs LiBrArY ShOuLd WoRk WiTh AnY StRiNg!
I tried with the "gsub" function but i found it really difficult to use.
This almost works:
original = "This LIBRARY should work with any string!"
print(original:gsub("(.)(.)",function (x,y) return x:upper()..y end))
It fails when the string has odd length and the last char is a letter, as in
original = "This LIBRARY should work with any strings"
I'll leave that case as an exercise.
First, split the string into an array of words:
local original = "This LIBRARY should work with any string!"
local words = {}
for v in original:gmatch("%w+") do
words[#words + 1] = v
end
Then, make a function to turn words like expected, odd characters to upper, even characters to lower:
function changeCase(str)
local u = ""
for i = 1, #str do
if i % 2 == 1 then
u = u .. string.upper(str:sub(i, i))
else
u = u .. string.lower(str:sub(i, i))
end
end
return u
end
Using the function to modify every words:
for i,v in ipairs(words) do
words[i] = changeCase(v)
end
Finally, using table.concat to concatenate to one string:
local result = table.concat(words, " ")
print(result)
-- Output: ThIs LiBrArY ShOuLd WoRk WiTh AnY StRiNg
Since I am coding mostly in Haskell lately, functional-ish solution comes to mind:
local function head(str) return str[1] end
local function tail(str) return substr(str, 2) end
local function helper(str, c)
if #str == 0 then
return ""
end
if c % 2 == 1 then
return toupper(head(str)) .. helper(tail(str),c+1)
else
return head(str) .. helper(tail(str), c+1)
end
end
function foo(str)
return helper(str, 1)
end
Disclaimer: Not tested, just showing the idea.
And now for real, you can treat a string like a list of characters with random-access with reference semantics on []. Simple for loop with index should do the trick just fine.

how to extracted updated string after calling string.gsub in lua?

Problem Description:
HI there. I'm trying to figure out how to use the lua function "string.gsub". I've been reading the manual which says:
This is a very powerful function and can be used in multiple ways.
Used simply it can replace all instances of the pattern provided with
the replacement. A pair of values is returned, the modified string and
the number of substitutions made. The optional fourth argument n can
be used to limit the number of substitutions made:
> = string.gsub("Hello banana", "banana", "Lua user")
Hello Lua user 1
> = string.gsub("banana", "a", "A", 2) -- limit substitutions made to 2
bAnAna 2
Question
When it says that a pair of values is returned; how do I get the new string value?
Code
local email_filename = "/var/log/test.txt"
local email_contents_file_exists = function(filename)
file = io.open(filename, "r")
if file == nil then
return false
else
file.close(file)
return true
end
end
local read_email_contents_file = function()
print('inside the function')
if not email_contents_file_exists(email_filename) then
return false
end
local f = io.open(email_filename, "rb")
local content = f:read("*all")
f:close()
print(content)
--content = string.gsub(content, '[username]', 'myusername')
--local tmp {}
--tmp = string.gsub(content, '[username]', 'myusername')
print(string.gsub(content, '[username]', 'myusername'))
return content
end
local test = read_email_contents_file()
What I've Tried So Far:
I've tried just printing the results, as you see above. That returns a bunch of garbled text. Tried saving to original string and I've also tried saving the results to an array (local tmp = {})
Any suggestions?
> = string.gsub('banana', 'a', 'A', 2)
bAnAna 2
> = (string.gsub('banana', 'a', 'A', 2))
bAnAna
You were going pretty good with reading the Lua users wiki.
In Lua, when you a function returns more than one value, you can access them all as follows
function sth()
return 1, "hi", false
end
x, y, z, a, b, c = sth() -- x = 1; y = "hi" and z = false(boolean); a = b = c = nil
Now, coming back to string.gsub function. It returns two values. The first being the processed string and the second being the number of time gsub performed itself on the input string.
So, to get the new string value, something like this would be best:
local tempString = string.gsub(content, '[username]', 'myusername')
OR
local tempString = content:gsub( '[username]', 'myusername' )
Ofcourse, here, you need to be aware about the various patterns used in Lua which are mentioned in the Programming in Lua book.
You need to escape [ and ] because they are magic characters in Lua patterns.

Resources