String concatenation? - ruby-on-rails

I am trying to understand string concatenation.
Why doesn't the fourth line give the same result as the second one?
counter = 0
"#{counter+1}. test" gives "1. test"
counter = 0
"#{++counter}. test" gives "0. test"

++ just looks like the increment operator. It's actually two unary + operators, so it's just the same as plain old counter

There is no ++ operator in Ruby. What ++counter says is "give me the positive result of the positive result of 0" which is 0.

++ is not an operator in Ruby. If you want to use a pre-increment operator then use:
counter += 1

Because, ++ is not an operator for Ruby like C or Java.

In Ruby, ++x or --x will do nothing! In fact, they behave as multiple unary prefix operators: -x == ---x == -----x == ...... or +x == +++x == +++++x == ......
To increment a number, simply write x += 1.
To decrement a number, simply write x -= 1.
Proof :
x = 1
+x == ++++++x # => true
-x == -----x # => true
x # => 1 # value of x doesn't change.

In C
++counter //Pre Increment
counter++// Post Incremet
But In Ruby ++ has no existence ,
So if you want to increment a variable then you have to simply write
counter = counter + 1
In your case you have to write just
"#{counter = counter + 1}. test" gives "1. test"
And will increment the value counter by 1

Related

Reliable way of getting the exact decimals from any number

I'm having problem returning spesific amount of decimal numbers from this function, i would like it to get that info from "dec" argument, but i'm stuck with this right now.
Edit: Made it work with the edited version bellow but isn't there a better way?
local function remove_decimal(t, dec)
if type(dec) == "number" then
for key, num in pairs(type(t) == "table" and t or {}) do
if type(num) == "number" then
local num_to_string = tostring(num)
local mod, d = math.modf(num)
-- find only decimal numbers
local num_dec = num_to_string:sub(#tostring(mod) + (mod == 0 and num < 0 and 3 or 2))
if dec <= #num_dec then
-- return amount of deciamls in the num by dec
local r = d < 0 and "-0." or "0."
local r2 = r .. num_dec:sub(1, dec)
t[key] = mod + tonumber(r2)
end
end
end
end
return t
end
By passing the function bellow i want a result like this:
result[1] > 0.12
result[2] > -0.12
result[3] > 123.45
result[4] > -1.23
local result = remove_decimal({0.123, -0.123, 123.456, -1.234}, 2)
print(result[1])
print(result[2])
print(result[3])
print(result[4])
I tried this but it seems to only work with one integer numbers and if number is 12.34 instead of 1.34 e.g, the decimal place will be removed and become 12.3. Using other methods
local d = dec + (num < 0 and 2 or 1)
local r = tonumber(num_to_string:sub(1, -#num_to_string - d)) or 0
A good approach is to find the position of the decimal point (the dot, .) and then extract a substring starting from the first character to the dot's position plus how many digits you want:
local function truncate(number, dec)
local strnum = tostring(number)
local i, j = string.find(strnum, '%.')
if not i then
return number
end
local strtrn = string.sub(strnum, 1, i+dec)
return tonumber(strtrn)
end
Call it like this:
print(truncate(123.456, 2))
print(truncate(1234567, 2))
123.45
1234567
To bulk-truncate a set of numbers:
local function truncate_all(t, dec)
for key, value in pairs(t) do
t[key] = truncate(t[key], dec)
end
return t
end
Usage:
local result = truncate_all({0.123, -0.123, 123.456, -1.234}, 2)
for key, value in pairs(result) do
print(key, value)
end
1 0.12
2 -0.12
3 123.45
4 -1.23
One could use the function string.format which is similar to the printf functions from C language. If one use the format "%.2f" the resulting string will contain 2 decimals, if one use "%.3f" the resulting string will be contain 3 decimals, etc. The idea is to dynamically create the format "%.XXXf" corresponding to the number of decimal needed by the function. Then call the function string.format with the newly created format string to generate the string "123.XXX". The last step would be to convert back the string to a number with the function tonumber.
Note that if one want the special character % to be preserved when string.format is called, you need to write %%.
function KeepDecimals (Number, DecimalCount)
local FloatFormat = string.format("%%.%df", DecimalCount)
local String = string.format(FloatFormat, Number)
return tonumber(String)
end
The behavior seems close to what the OP is looking for:
for Count = 1, 5 do
print(KeepDecimals(1.123456789, Count))
end
This code should print the following:
1.1
1.12
1.123
1.1235
1.12346
Regarding the initial code, it's quite straight-forward to integrate the provided solution. Note that I renamed the function to keep_decimal because in my understanding, the function will keep the requested number of decimals, and discard the rest.
function keep_decimal (Table, Count)
local NewTable = {}
local NewIndex = 1
for Index = 1, #Table do
NewTable[NewIndex] = KeepDecimal(Table[Index], Count)
NewIndex = NewIndex + 1
end
return NewTable
end
Obviously, the code could be tested easily, simply by copy and pasting into a Lua interpreter.
Result = keep_decimal({0.123, -0.123, 123.456, -1.234}, 2)
for Index = 1, #Result do
print(Result[Index])
end
This should print the following:
0.12
-0.12
123.46
-1.23
Edit due to the clarification of the need of truncate:
function Truncate (Number, Digits)
local Divider = Digits * 10
local TruncatedValue = math.floor(Number * Divider) / Divider
return TruncatedValue
end
On my computer, the code is working as expected:
> Truncate(123.456, 2)
123.45

Generating star pattern in LUA

I am new to programming in LUA. And I am not able to solve this question below.
Given a number N, generate a star pattern such that on the first line there are N stars and on the subsequent lines the number of stars decreases by 1.
The pattern generated should have N rows. In every row, every fifth star (*) is replaced with a hash (#). Every row should have the required number of stars (*) and hash (#) symbols.
Sample input and output, where the first line is the number of test cases
This is what I tried.. And I am not able to move further
function generatePattern()
n = tonumber(io.read())
i = n
while(i >= 1)
do
j = 1
while(j<=i)
do
if(j<=i)
then
if(j%5 == 0)
then
print("#");
else
print("*");
end
print(" ");
end
j = j+1;
end
print("\n");
i = i-1;
end
end
tc = tonumber(io.read())
for i=1,tc
do
generatePattern()
end
First, just the stars without hashes. This part is easy:
local function pattern(n)
for i=n,1,-1 do
print(string.rep("*", i))
end
end
To replace each 5th asterisk with a hash, you can extend the expression with the following substitution:
local function pattern(n)
for i=n,1,-1 do
print((string.rep("*", i):gsub("(%*%*%*%*)%*", "%1#")))
end
end
The asterisks in the pattern need to be escaped with a %, since * holds special meaning within Lua patterns.
Note that string.gsub returns 2 values, but they can be truncated to one value by adding an extra set of parentheses, leading to the somewhat awkward-looking form print((..)).
Depending on Lua version the metamethod __index holding rep for repeats...
--- Lua 5.3
n=10
asterisk='*'
print(asterisk:rep(n))
-- puts out: **********
#! /usr/bin/env lua
for n = arg[1], 1, -1 do
local char = ''
while #char < n do
if #char %5 == 4 then char = char ..'#'
else char = char ..'*'
end -- mod 5
end -- #char
print( char )
end -- arg[1]
chmod +x asterisk.lua
./asterisk.lua 15
Please do not follow this answer since it is bad coding style! I would delete it but SO won't let me. See comment and other answers for better solutions.
My Lua print adds newlines to each printout, therefore I concatenate each character in a string and print the concatenated string out afterwards.
function generatePattern()
n = tonumber(io.read())
i = n
while(i >= 1)
do
ouput = ""
j = 1
while(j<=i)
do
if(j%5 == 0)
then
ouput=ouput .. "#";
else
ouput=ouput .. "*";
end
j = j+1;
end
print(ouput);
i = i-1;
end
end
Also this code is just yours minimal transformed to give the correct output. There are plenty of different ways to solve the task, some are faster or more intuitive than others.

How to create variable names that are concatenated with a increasing number

I am currently programming a Lua script. There I would like to have a variable name which is concatenated with an increasing number.
Example: Q0001,Q0002,Q0003,...,Q9999
My following script for this is:
local rnd = math.random (0,9999)
local Text = ""
print(rnd)
if rnd > 0 and rnd < 10 then
--Add Nulls before Number and the "Q"
Text = Q000 .. rnd
elseif rnd >= 10 and rnd < 100 then
--Add Nulls before Number and the "Q"
Text = Q00 .. rnd
elseif rnd >= 100 and rnd < 1000 then
--Add Null before Number and the "Q"
Text = Q0 .. rnd
elseif rnd >= 1000 then
--Add "Q"
Text = Q .. rnd
end
print(Text)
Logically I put this into a function, because its only a part of my programm. Later in the programm I like to get informations with the variable, because the product of the variable Q### is a table which I have programmed. My second thought to solve the problem was to convert it as a text, but then I don't know how to convert this into a declaration.
Edit 04/04/15 19:17: Too make it more clear. I want that Text stands after the end of the script for a table I set before. So that I can say Text.Name e.g.
Use string.format with padded format specifiers:
Just a single line:
Text = ("Q%04d"):format( rnd )
-- same as Text = string.format( "Q%04d", rnd )
Instead of creating so many tables, use a single table with the above values as keys/indexes:
t = {
Q0001 = "something",
Q0002 = "something",
Q0013 = "something",
Q0495 = "something",
-- so on
}

How to convert a Lua string to float

I am writing a simple Lua script to calculate a median from a Sorted Set (http://redis.io/commands/#sorted_set) within Redis 2.8. The script is below
local cnt = redis.call("ZCARD", KEYS[1])
if cnt > 0 then
if cnt%2 > 0 then
local mid = math.floor(cnt/2)
return redis.call("ZRANGE", KEYS[1], mid, mid)
else
local mid = math.floor(cnt/2)
local vals = redis.call("ZRANGE", KEYS[1], mid-1, mid)
return (tonumber(vals[1]) + tonumber(vals[2]))/2.0
end
else
return nil
end
The problem is the script always returns an integer, when the result should be a float. The result is wrong.
$ redis-cli zrange floats 0 100
1) "1.1"
2) "2.1"
3) "3.1"
4) "3.4975"
5) "42.63"
6) "4.1"
$ redis-cli EVAL "$(cat median.lua)" 1 floats
(integer) 3
The correct result should be (3.1 + 3.4975)/2.0 == 3.298
From the documentation for EVAL:
Lua has a single numerical type, Lua numbers. There is no distinction between integers and floats. So we always convert Lua numbers into integer replies, removing the decimal part of the number if any. If you want to return a float from Lua you should return it as a string, exactly like Redis itself does (see for instance the ZSCORE command).
Therefore, you should update your script so you are returning the float as a string:
return tostring((tonumber(vals[1]) + tonumber(vals[2]))/2.0)
sorry i have no explanation of this i encounter this by accidental when making script
function int2float(integer)
return integer + 0.0
end

Calculate letter grade using a series of grades

Noob to Ruby here. Working through some exercises and have hit a wall.
Exercise: Calculate the letter grade of a series of grades
Create a method get_grade that accepts an Array of test scores. Each score in the array should be between 0 and 100, where 100 is the max score.
Compute the average score and return the letter grade as a String, i.e., 'A', 'B', 'C', 'D', 'E', or 'F'.
I keep returning the error:
avg.rb:1: syntax error, unexpected tLBRACK, expecting ')'
def get_grade([100,90,80])
^
avg.rb:1: syntax error, unexpected ')', expecting $end
Here's what I have so far. I'd like to stick with the methods below or .join as I'm trying to work with the methods we're learning in class. So sum, inject, etc won't necessarily be helpful. And I apologize in advance for the specificity of the request :) I'm sure there's a way better way that is way less code, but I'm just trying to learn it this way to start.
def get_grade([100,90,80])
get_grade = (array[0] + array[1] + array[2]).to_i / array.length.to_i
case get_grade
when 90..100
"A"
when 80..90
"B"
when 70..80
"C"
when 60..70
"D"
when 0..60
"F"
else
"Error"
end
end
puts get_grade([100,90,80])
You can't just randomly dump an array literal like [100,90,80] into the parameter list of a function definition. Judging by the function body, I think you meant to accept a single parameter array:
def get_grade(array)
grade = (array[0].to_i + array[1].to_i + array[2].to_i) / array.length
case grade
# unchanged
end
end
A terse replacement of the big case statement, for fun:
def letter_grade( score ) # assumes that score is between 0 and 100 (not 0-1)
%w[F F F F F F D C B A][ (score/10.0).floor ] || 'A' # handles grades >=100
end
Or, for more granularity:
def letter_grade( score ) # score is between 0 and 100 (not 0-1)
grades = %w[F F F F F F F F F F F F F F F F F F D- D D+ C- C C+ B- B B+ A- A A+ A+]
grades[ (3.0*score/10).floor ]
end
Thanks for the help today! Here's what I ended up doing to make it work with more than just 3 arguments. I used an Array#each method. I imagine there's a more elegant solution out there, but it worked! Worked on this since 10:00 AM, greatly appreciate the help!
def get_grade(array)
sum = 0
array.each do |element|
sum += element
end
average = sum / array.length
if average >= 90
grade = "A"
elsif average >= 80
grade = "B"
elsif average >= 70
grade = "C"
elsif average >= 60
grade = "D"
elsif average >= 0
grade = "F"
else
"Error"
end
end
puts get_grade([70,80,80,90,100])
puts get_grade([100,80,90,11,20])
puts get_grade([30,20,10,60,75])
Remember that the max score is 100 (and it can be assumed that the min is 0).
def get_grade(array)
sum = 0
array.each do |x|
sum += x
end
average = sum / array.length
if average > 100
print "Grades must be no more than 100!"
elsif average >= 90
grade = "A"
elsif average >= 80
grade = "B"
elsif average >= 70
grade = "C"
elsif average >= 60
grade = "D"
elsif average >=0
grade = "F"
else
print "Grades must be no less than 0!"
end
grade
end
puts get_grade([100,90,80]) == "A"
puts get_grade([98,90,80]) == "B"
puts get_grade([80,80,80]) == "B"
puts get_grade([55,45,35]) == "F"
puts get_grade([101,100,104])
puts get_grade([-2,-3,-4])
Added a proc so that even if a user enters a score over 100 it won't be calculated into the average.
Also refactored the switch statements to one line each. Let me know if this helps. Good luck.
def get_grade array
scores_under_100 = Proc.new {|score| score <= 100 && score > 0}
scores = array.select(&scores_under_100)
average = scores.inject(:+) / scores.size
case average
when 90..100 then puts "A."
when 80..89 then puts "B."
when 70..79 then puts "C."
when 60..69 then puts "D."
else puts "F."
end
end
puts get_grade([100, 100, 90, 67, 85, 200, 290, 299, 299])

Resources