adequate solution to hand over variables in nested functions (in ruby) - ruby

I have several step_1-methods which are based on x and y variables.
step_2 creates new methods based on step_1-methods but the variables aren't needed (just passed through)!
The same goes for step_3 (based on step_2-methods).
My problem is I have about 20 step_2-methods which consists dozens of step_1-methods (5 differents kinds). And for each one I have to pass the same two variables.
I need this kind of construction for iteration purposes.
Now, is there a way to hand over the variables directly form step_3(x, y) to step_1 (x, y) without using global variables?
# example
def step_1 (x, y)
return x + y
end
def step_2 (*foo)
return step_1(*foo)
end
def step_3 (*foo)
return step_2(*foo)
end
x, y = 2, 2 # example
puts step_3(x, y) # ==> 4
Thanks for any suggestion

When I read "I have to pass the same two variables", this naturally brings to mind the idea of creating a simple container you can pass around instead:
class NumberTuple
attr_accessor :x
attr_accessor :y
def initialize(x, y)
#x = x
#y = y
end
end
tuple = NumberTuple.new(2,2)
step_3(tuple)
This often leads to the conclusion that creating a simple calculation class that can internalize all of this state. That's what class instances excel at:
class NumberCalculator
def initialize(x, y)
#x = x
#y = y
end
def step_3
step_2
end
def step_2
step_1
end
def step_1
#x + #y
end
end
calculator = NumberCalculator.new(2,2)
calculator.step_3

alias step_3 :step_1
or if you want to go through intermediate steps,
alias step_2 :step_1
alias step_3 :step_2

Related

is '#client command' or '#client event' with if statements more efficient (discord.py)

Just a quick question which I cannot find the answer too.
which piece of code is more efficient/faster
this:
#client.event
async def message(message):
if message.content == 'x':
await message.channel.send('y')
or this:
#client.command()
async def x(ctx):
await ctx.send('y')
syntax might be a little off for the client.command() part, I have only used client.event method so far.
Before answering, it's important to understand, on_message is the base of commands extension.
What it does is
commands = {} # example dictionary
#bot.command() # this registers the function in the command dictionary
async def test(ctx: commands.Context):
# some code here
# after this line is executed, your commands dictionary would look somethig like
# commands = {'test': test} (here, test is coroutine function you made)
on_message looks for a message.content, if it starts with the prefix, it gets the message without the prefix, then checks if the command called is in the dictionary, if yes, then call the command on that part with the arguments provided. If not, raise commands.CommandsNotFound
The above on_message I spoke about is the default on_message event bound to the bot. This is also a reason why commands extension doesn't work if one overrides the on_message event
Now with that out of the way, python dictionaries are implemented as hash tables. If the amount of if else staement is less then yes, they might be equally performative but in the longer run, dictionary lookup will prevail. Thus, I would say the commands extesnion is better to use.
In addition to the long run benefit, commands extension offers a much better way to manage your code.
if x == "test":
s = 1
y = 1
j = 1
print(s, y, z)
elif x == "test2":
s = 2
y = 2
j = 2
print(s, y, z)
elif x == "test3":
s = 3
y = 3
j = 3
print(s, y, z)
elif x == "test4":
s = 4
y = 4
j = 4
print(s, y, z)
elif x == "test5":
s = 5
y = 5
j = 5
print(s, y, z)
is certainly not appealing comapred to
def test1():
s = 1
y = 1
j = 1
print(s, y, z)
def test2():
s = 2
y = 2
j = 2
print(s, y, z)
def test3():
s = 3
y = 3
j = 3
print(s, y, z)
def test4():
s = 4
y = 4
j = 4
print(s, y, z)
def test5():
s = 5
y = 5
j = 5
print(s, y, z)
And it's also much easier to navigate and maintain your code with the command extension!
So, there is no reason for anyone to use if else in on_message to create commands when given a superior choice, i.e commands extesnion

Why symbol is not equal to symbol

I did:
x = :foo, y = %q{foo}.to_sym, x.==(y)
# => [:foo, :foo, false]
I am wondering why :foo is not equal to :foo.
Parallel assignment works differently in Ruby, and if you want multiple statements on one line, you need to separate them with semicolons (not commas).
The statement you executed:
x = :foo, y = %q{foo}.to_sym, x == y
Is equivalent to:
x = [:foo, y = %q{foo}.to_sym, x == y]
N.B. x is not yet defined when the right-hand side of the expression is evaluated, so the last term is effectively comparing nil and :foo. The y assignment also happens while the right-hand side of the expression is evaluating, and the result gets included in the array literal being assigned to x.
Here's what you meant to do:
x, y = :foo, %q{foo}.to_sym; x == y # => true
Just an aside, x.==(y) works because :== is technically a method, but in idiomatic Ruby you just write x == y. The interpreter knows what you mean.
Nice answer, but there is a logical leap from "x is not yet defined" to "the last term is effectively comparing nil and :foo.", in particular, why an undefined variable is evaluated as nil is unexplained.
This has to do with how variable hoisting is implemented in Ruby. It's an obnoxious feature of JavaScript too.

Is there a way I make a defined variable reset every time I use it?

This is the code
x = 10
y = 11
def z(q,x,y)
q = q - (x + y)
end
q = 30
z(q,x,y)
puts "#{q}"
q = 40
z(q,x,y)
puts "#{q}"
Is there a way that z(q,x,y) would reset every time I use it?
Try this:
x = 10
y = 11
def z(q,x,y)
q = q - (x + y)
end
q = 30
z1 = z(q,x,y)
puts "#{z1}"
q = 40
z2 = z(q,x,y)
puts "#{z2}"
prints (using puts):
9
19
Generally, you shouldn't modify params. In function z, q is a parameter that's been set but I think you are also wanting it to be an outside variable and mutate its state.
Here's what you want to do but please do not do this.
x = 10
y = 11
def z(q,x,y)
$q = q - (x + y)
end
$q = 30
z($q,x,y)
puts "#{$q}"
$q = 40
z($q,x,y)
puts "#{$q}"
# Prints:
# 9
# 19
This is probably bad code. Why? Globals are bad. Mutating state like this when we are blessed to have a mathematical function so pure like this is a war crime and might cause rioting. We have a math function that doesn't need to change state. We don't really want z to be responsible for:
computing a new value
setting a local variable for use later
The second part is the damning part. Z knows about its outside world when it doesn't need to.
Look at this which is similar to the other answer already here:
x = 10
y = 11
def z(q, x, y)
q - (x + y)
end
q = z(30, x, y)
puts q
q = z(40, x, y)
puts q
If you are starting out with Ruby or programming in general, read a lot of code and watch screencasts of people working live. It's the fastest route to leveling up a little bit. It's ok if it doesn't happen right away, the next time you try will be easier (years later maybe).

How do I create arithmetic statements using variables?

I'm trying to create a method to check if three variables a, b and c are a pythagorean triplet. I set it up with a known triplet: 3, 4, 5. This program won't run though and I can't figure out why.
a = 3
b = 4
c = 5
def triplet?
if a**2 + b ** 2 == c ** 2
puts 'pythagorean triplet'
else puts 'not a pythagorean triplet'
end
end
triplet?
It returns the error message:
undefined local variable or method `a' for main:Object (NameError)
Any help will be much appreciated.
a, b, and c are local to the scope they're defined in, and thus aren't visible to separate scopes (such as other methods). See the doc on Object#def:
Starts a new local scope; local variables in existence when the def block is entered are not in scope in the block, and local variables created in the block do not survive beyond the block.
What you want to do is pass numbers as parameters:
def triplet?(a, b, c)
if a**2 + b ** 2 == c ** 2
puts 'pythagorean triplet'
else puts 'not a pythagorean triplet'
end
end
triplet?(3, 4, 5)
This will define those three variables in the scope of the triplet? method, then you populate their values by passing them when you invoke the method.
A small point of note, by convention, predicate methods (that is, methods ending in ?) in Ruby conventionally return a boolean. To write this method idiomatically, you might say:
def triplet?(a, b, c)
a**2 + b ** 2 == c ** 2
end
if triplet?(3, 4, 5)
puts 'pythagorean triplet'
else
puts 'not a pythagorean triplet'
end
That way, triplet? will always return a boolean true or false, then you can use it in your code to write English-y sentences.
Within the definition block, which is the scope for local variables, a is not defined, hence the error message.
a = 3
b = 4
c = 5
def triplet?(a, b, c)
if a**2 + b ** 2 == c ** 2
puts 'pythagorean triplet'
else
puts 'not a pythagorean triplet'
end
end
triplet?(a, b, c)
def creates a function. Inside the function block, you have a scope. a, b, and c are not in that scope. Tell the function to take parameters a, b, c and pass it the parameters.
There is no relation between the name you give the function parameters and the function parameters you pass.
The following will also work:
x = 3
y = 4
z = 5
def triplet?(a, b, c)
if a**2 + b ** 2 == c ** 2
puts 'pythagorean triplet'
else
puts 'not a pythagorean triplet'
end
end
triplet?(x, y, z)

Reading lists from a file in Ruby

I have a txt file which contains data in the following format:
X1 Y1
X2 Y2
etc..
I want to read the data from this file and create two lists in ruby (X containing X1, X2 and Y containing Y1, Y2). How can I do this in Ruby?
Thanks.
A real one-liner:
x, y = File.foreach("filename").collect {|line| line.split}.transpose
Pseudocode
File.new("source.txt", "r").each_line do |line|
x, y = line.split
xs << x
ys << y
end
You might want to checkout the Rdoc for detail API.
I prefer using the readlines method for things such as this.
x = []
y = []
File.readlines(filename).each do |line|
x << line.split[0]
y << line.split[1]
end
As Mladen (from the comments of this answer) suggests, I am splitting it twice which is probably slower than assigning it to a variable and referencing that. He also mentions that using foreach is better than readlines, and I agree. Using their advice, this is how we would both go about doing it:
x = []
y = []
File.foreach(filename).each do |line|
line = line.split
x << line[0]
y << line[1]
end
Something like this if you have exactly two columns:
one = Array.new
two = Array.new
File.open("filename") do |file|
while line = file.gets
one << line.split[0]
two << line.split[1]
end
end

Resources