Get the range of string in lua in row and column - lua

I'm trying to calculate the range of a given text in terms of row and column.
For following string,
'hello\nworld
The range should be
{
row_start = 0,
col_start = 0,
row_end = 1,
col_end = 4
}
Here, row_start and col_start are NOT important for the question. world will be in the second line hens the row_end is 1. world has 5 characters hens the col_end is 4.
So, I need a function to calculate the number of line breaks and length of the string at the last line to calculate the range.
I couldn't find any other way than calculating the number of line breaks to get row_end. Then reverse the text and find the index of the first newline character to get the col_end. Any other efficient way to do this in Lua?

Given str = "hello\nworld":
I couldn't find any other way than calculating the number of line breaks to get row_end
There is no more efficient way: You have to count the line breaks. Assuming UNIX LFs as in your example, you can simply use gmatch for this (which is presumably more efficient than abusing gsub to do the counting for you):
local row_end = 0
for _ in str:gmatch"\n" do row_end = row_end + 1 end
Then reverse the text and find the index of the first newline character to get the col_end. Any other efficient way to do this in Lua?
Yes, this is indeed needlessly inefficient. The shortest way to do this Lua would be using pattern matching:
local col_end = #str - str:find"[^\n]*$"
Explanation: Find the starting index of the longest "run" of non-newline characters. For str, this would be the index of w. Then subtract this index from the length of the string to find the 0-based index of the last character.
A probably more efficient solution would just remember the index after the last newline (and thus have no issue with possibly poor pattern matching performance):
local after_last_newline_idx = 1
for idx in str:gmatch"\n()" do -- () captures the position after the newline
after_last_newline_idx = idx
end
local col_end = #str - after_last_newline_idx
This could be merged with the first loop to only loop once:
local row_end = 0
local after_last_newline_idx = 1
for idx in str:gmatch"\n()" do -- () captures the position after the newline
row_end = row_end + 1
after_last_newline_idx = idx
end
local col_end = #str - after_last_newline_idx
... taking linear time, which is required. However this avoids creating a garbage string by reversing str. It only loops over the string once to find newlines. If gmatch is too slow for your purposes, you can easily use string:byte or string:sub and a numeric for loop to do the looping over newlines yourself.

Related

Printing the required number of inverted star patterns [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
An inverted star pattern consists of a given number of rows of asterisks; in which each next row has one asterisk fewer, until one asterisk in the last row remains; with every fifth asterisk replaced with a # (see image):
****#*
****#
****
***
**
*
User is prompted to enter the number of star patterns and then, for each pattern, its width, which is the same as height. Then the required number of patterns of needed size, is to be output.
Well, until the part "without using variable as limit" is clarified, it's
-- Localise functions that are called several times, for performance.
local floor = math.floor
local len, sub, rep = string.len, string.sub, string.rep
local insert, concat = table.insert, table.concat
local function pattern (size, template)
-- Repeat template as many times as necessary to cover size and cut off what is not needed:
local line = sub (rep (template, floor (size / len (template) ) + 1 ), 1, size)
local lines = {}
while (len (line) > 0) do
insert (lines, line)
line = sub (line, 1, -2) -- cut the last character off.
end
return concat(lines, '\n')
end
io.write ('How many inverted star patterns do you want? ')
local no = tonumber (io.read ()) or 0
local patterns = {}
for i = 1, no do
io.write ('How many rows in pattern no. ' .. tostring (i) .. '? ')
patterns[i] = pattern (tonumber (io.read ()) or 0, '****#')
end
print (concat (patterns, '\n'))
Note that rows of the star pattern and the patterns themselves are stored as tables that are concatenated with table.concat only when they are complete, rather than as strings appended on any iteration using ... This is faster, since in Lua a string will be re-allocated on each concatenation.
And this is an alternative implementation of the pattern function:
local function pattern (size, template)
local length = len (template)
local rows = {}
for i = size, 1, -1 do
-- Repeat template as many times as necessary to cover i and cut off what is not needed:
insert (rows, sub (rep (template, floor (i / length) + 1), 1, i))
end
return concat (rows, '\n')
end
And this is another implementation, where pattern rows are cached, which might speed the program up:
local cache = {}
local function pattern (size, template)
local length = len (template)
local rows = {}
for i = size, 1, -1 do
-- Repeat template as many times as necessary to cover i and cut off what is not needed.
-- And cache it.
cache[i] = cache[i] or sub (rep (template, floor (i / length) + 1), 1, i)
insert (rows, cache[i])
end
return concat (rows, '\n')
end

How to refactor string containing variable names into booleans?

I have an SPSS variable containing lines like:
|2|3|4|5|6|7|8|10|11|12|13|14|15|16|18|20|21|22|23|24|25|26|27|28|29|
Every line starts with pipe, and ends with one. I need to refactor it into boolean variables as the following:
var var1 var2 var3 var4 var5
|2|4|5| 0 1 0 1 1
I have tried to do it with a loop like:
loop # = 1 to 72.
compute var# = SUBSTR(var,2#,1).
end loop.
exe.
My code won't work with 2 or more digits long numbers and also it won't place the values into their respective variables, so I've tried nest the char.substr(var,char.rindex(var,'|') + 1) into another loop with no luck because it still won't allow me to recognize the variable number.
How can I do it?
This looks like a nice job for the DO REPEAT command. However the type conversion is somewhat tricky:
DO REPEAT var#i=var1 TO var72
/i=1 TO 72.
COMPUTE var#i = CHAR.INDEX(var,CONCAT("|",LTRIM(STRING(i,F2.0)),"|"))>0).
END REPEAT.
Explanation: Let's go from the inside to the outside:
STRING(value,F2.0) converts the numeric values into a string of two digits (with a leading white space where the number consist of just one digit), e.g. 2 -> " 2".
LTRIM() removes the leading whitespaces, e.g. " 2" -> "2".
CONCAT() concatenates strings. In the above code it adds the "|" before and after the number, e.g. "2" -> "|2|"
CHAR.INDEX(stringvar,searchstring) returns the position at which the searchstring was found. It returns 0 if the searchstring wasn't found.
CHAR.INDEX(stringvar,searchstring)>0 returns a boolean value indicating if the searchstring was found or not.
It's easier to do the manipulations in Python than native SPSS syntax.
You can use SPSSINC TRANS extension for this purpose.
/* Example data*/.
data list free / TextStr (a99).
begin data.
"|2|3|4|5|6|7|8|10|11|12|13|14|15|16|18|20|21|22|23|24|25|26|27|28|29|"
end data.
/* defining function to achieve task */.
begin program.
def runTask(x):
numbers=map(int,filter(None,[i.strip() for i in x.lstrip('|').split("|")]))
answer=[1 if i in numbers else 0 for i in xrange(1,max(numbers)+1)]
return answer
end program.
/* Run job*/.
spssinc trans result = V1 to V30 type=0 /formula "runTask(TextStr)".
exe.

Non numeral indeces and the # never counts?

Given a table with mixed indexes like:
table = {
foo = 'bar'
[1] = 'foobar'
}
My question is about the # which gives the last index which is not separate through a gap while iterating through the table.
print(#table)
will give the output 1.
table = {
foo = 'bar',
lol = 'rofl',
[1] = 'some',
[2] = 'thing',
[3] = 'anything',
[4] = 'else'
}
print(#table)
should print 4
Can I be 100% sure that the # will never be distracted by non-numeral indexes?
Are those indexes really unregarded at every time?
Yes, you can count on that (in lua 5.1).
From the lua reference manual:
The length operator is denoted by the unary operator #. The length of
a string is its number of bytes (that is, the usual meaning of string
length when each character is one byte).
The length of a table t is defined to be any integer index n such that
t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be
zero. For a regular array, with non-nil values from 1 to a given n,
its length is exactly that n, the index of its last value. If the
array has "holes" (that is, nil values between other non-nil values),
then #t can be any of the indices that directly precedes a nil value
(that is, it may consider any such nil value as the end of the array).
lua 5.2 allows for the __len metamethod to operate on tables and that means # can do other things. See #kikito's answer for some examples.
Etan answer is correct, but not complete.
In Lua, if a table's metatable has a __len function, it will control what the # operator spits out. One can define it so that it takes into account the non-array keys.
local mt = {__len = function(tbl)
local len = 0
for _ in pairs(tbl) do len = len + 1 end
return len
end}
This demonstrates the thing:
local t = {1,2,3,4,foo='bar',baz='qux'}
print(#t) -- 4
setmetatable(t, mt)
print(#t) -- 6
If you really want to make sure that you get the "proper" array-like length, you must use rawlen instead:
print(rawlen(t)) -- 4, even with the metatable set
Edit: Note that __len does not work as I mention on Lua 5.1
The only way is to iterate through entries and count them. Iterate with ipair through the item and increment counter then return result.
function tablelength(T)
local count = 0 for _ in pairs(T) do
count = count + 1 end
return count
end
The # operator only work for hash table type.
See: How to get number of entries in a Lua table?

What does # mean in Lua?

I have seen the hash character '#' being added to the front of variables a lot in Lua.
What does it do?
EXAMPLE
-- sort AIs in currentlevel
table.sort(level.ais, function(a,b) return a.y < b.y end)
local curAIIndex = 1
local maxAIIndex = #level.ais
for i = 1,#currentLevel+maxAIIndex do
if level.ais[curAIIndex].y+sprites.monster:getHeight() < currentLevel[i].lowerY then
table.insert(currentLevel, i, level.ais[curAIIndex])
curAIIndex = curAIIndex + 1
if curAIIndex > maxAIIndex then
break
end
end
end
Apologies if this has already been asked, I've searched around on the internet a lot but I haven't seem to have found an answer. Thanks in advance!
That is the length operator:
The length operator is denoted by the unary operator #. The length of a string is its number of bytes (that is, the usual meaning of string length when each character is one byte).
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be zero. For a regular array, with non-nil values from 1 to a given n, its length is exactly that n, the index of its last value. If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
# is the lua length operator which works on strings or on table arrays
Examples:
print(#"abcdef") -- Prints 6
print(#{"a", "b", "c", 88}) -- Prints 4
-- Counting table elements is not suppoerted:
print(#{["a"]=1, ["b"]=9}) -- # Prints 0
#is most often used to get the range of a table. For example:
local users = {"Grace", "Peter", "Alice"}
local num_users = #users
print("There is a total of ".. num_users)
Output:
3

Lua base converter

I need a base converter function for Lua. I need to convert from base 10 to base 2,3,4,5,6,7,8,9,10,11...36 how can i to this?
In the string to number direction, the function tonumber() takes an optional second argument that specifies the base to use, which may range from 2 to 36 with the obvious meaning for digits in bases greater than 10.
In the number to string direction, this can be done slightly more efficiently than Nikolaus's answer by something like this:
local floor,insert = math.floor, table.insert
function basen(n,b)
n = floor(n)
if not b or b == 10 then return tostring(n) end
local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local t = {}
local sign = ""
if n < 0 then
sign = "-"
n = -n
end
repeat
local d = (n % b) + 1
n = floor(n / b)
insert(t, 1, digits:sub(d,d))
until n == 0
return sign .. table.concat(t,"")
end
This creates fewer garbage strings to collect by using table.concat() instead of repeated calls to the string concatenation operator ... Although it makes little practical difference for strings this small, this idiom should be learned because otherwise building a buffer in a loop with the concatenation operator will actually tend to O(n2) performance while table.concat() has been designed to do substantially better.
There is an unanswered question as to whether it is more efficient to push the digits on a stack in the table t with calls to table.insert(t,1,digit), or to append them to the end with t[#t+1]=digit, followed by a call to string.reverse() to put the digits in the right order. I'll leave the benchmarking to the student. Note that although the code I pasted here does run and appears to get correct answers, there may other opportunities to tune it further.
For example, the common case of base 10 is culled off and handled with the built in tostring() function. But similar culls can be done for bases 8 and 16 which have conversion specifiers for string.format() ("%o" and "%x", respectively).
Also, neither Nikolaus's solution nor mine handle non-integers particularly well. I emphasize that here by forcing the value n to an integer with math.floor() at the beginning.
Correctly converting a general floating point value to any base (even base 10) is fraught with subtleties, which I leave as an exercise to the reader.
you can use a loop to convert an integer into a string containting the required base. for bases below 10 use the following code, if you need a base larger than that you need to add a line that mapps the result of x % base to a character (usign an array for example)
x = 1234
r = ""
base = 8
while x > 0 do
r = "" .. (x % base ) .. r
x = math.floor(x / base)
end
print( r );

Resources