I like to use tap occasionally as a beautifier for method returns. However, when using tap with a recursive function, it's behavior is different than what I expect:
class Node
attr_accessor :name, :children
def initialize(name); self.name, self.children = name, []; end
def render
res = "Name: #{name}\n"
children.each do |child|
res += " - " + child.render + "\n"
end
res
end
end
parent = Node.new('Parent')
parent.children = [Node.new('Child')]
puts parent.render
Returns
Name: Parent
- Name: Child
If I change the render function to use tap:
def render
"Name: #{name}\n".tap do |res|
children.each do |child|
res += " - " + child.render + "\n"
end
end
end
It returns
Name: Parent
I would assume the behavior would be identical to the first render function. The docs indicate it "Yields x to the block, and then returns x"...since the function is recursing is it somehow polluting the function stack?
This has nothing to do with anything except that assignment changes a variable, and variables are passed by value. tap is irrelevant, the behavior is identical if you place a string into any variable.
In your case, you're passing a string literal into a proc, which receives a variable called res containing a copy of that string. You're then modifying that variable, not the original string itself.
Consider:
def test(res)
res += "bar"
end
x = "foo"
test(x)
puts x # outputs "foo", not "foobar"
The reason your first example works is that you're replacing the value in the string res with a new value. You're not actually appending data to the string stored in res.
Related
If I have the following:
puts "---doesn't change---"
def change_string(val)
val = "abc"
end
str = "some"
change_string(str)
puts str
puts "---does change---"
def change_string_two(val)
puts "val is of class: #{val.class}"
val << " extra"
puts "val is of class: #{val.class}"
end
change_string_two(str)
puts str
puts "str is of type: #{str.class}"
puts "---does change---"
str = "xxx"
def change_string_three(val)
val.concat(" more things")
end
change_string_three(str)
puts str
It ouptuts:
---doesn't change---
some
---does change---
val is of class: String
val is of class: String
some extra
str is of type: String
---does change---
xxx more things
I understand that it's passing an object reference but I'm confused as to how in one scenario it doesn't change and in two scenarios, it does change. Is this peculiar to strings in Ruby?
This is not peculiar to Ruby. val.concat("x") and val << "x" modify the value val refers to. Whenever you have a function where the argument being passed is a reference, then methods called on the argument which mutate it will result in mutations on the originally referenced thing.
An assignment like val = "abc" on the other hand reassigns the local variable val to refer to something new, and leaves what it previously referred to untouched.
If you want to replace the string rather than add to it, use val.replace("abc"), e.g.
def replace(val)
val.replace("abc")
end
str = "123"
replace(str)
puts str
outputs "abc".
I found following code at the internet
class Test
def value
'string'
end
def inspect
'value'
end
end
def test(arg)
arg.tap { |i| i.value }
end
p test(Test.new)
Could anyone explain why it returns
p test(Test.new)
# >> value
Because arg.tap returns arg itself, and p prints arg.inspect, since you have overwritten theinspect method of Test, it returns a string 'value', so you the print result is value
see also:
- p vs puts in Ruby
- tap method
tap used for chain of methods. It runs the passed blocked and returns the input without any change.
It means
arg.tap { |i| i.value } will return arg
p method runs inspect method of passed object
There are many examples how to pass Ruby block as an argument, but these solutions pass the block itself.
I need a solution that takes some variable, executes an inline code block passing this variable as a parameter for the block, and the return value as an argument to the calling method. Something like:
a = 555
b = a.some_method { |value|
#Do some stuff with value
return result
}
or
a = 555
b = some_method(a) { |value|
#Do some stuff with value
return result
}
I could imagine a custom function:
class Object
def some_method(&block)
block.call(self)
end
end
or
def some_method(arg, &block)
block.call(arg)
end
but are there standard means present?
I think, you are looking for instance_eval.
Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj’s instance variables. In the version of instance_eval that takes a String, the optional second and third parameters supply a filename and starting line number that are used when reporting compilation errors.
a = 55
a.instance_eval do |obj|
# some operation on the object and stored it to the
# variable and then returned it back
result = obj / 5 # last stament, and value of this expression will be
# returned which is 11
end # => 11
This is exactly how #Arup Rakshit commented. Use tap
def compute(x)
x + 1
end
compute(3).tap do |val|
logger.info(val)
end # => 4
I have a Ruby application that I'm developing that, for some reason, does not work as expected when using a recursive function that contains a block inside to return a value from a different class' function call (easier to see in the example code below). The odd thing is that when I created a minimal sample to try and find out what was going on, the sample works as expected. Example:
require 'json'
class Simple
attr_accessor :name, :children
def initialize(name,children=nil)
#name = name
#children = children
end
end
a = Simple.new('A')
b = Simple.new('B',[a])
c = Simple.new('C',[b])
d = Simple.new('D')
e = Simple.new('E',[d])
f = Simple.new('F')
g = Simple.new('G',[e,f])
foo = [c,e,g]
def looper(d)
holder = nil
d.each do |item|
# puts item.name
if item.name == 'D'
holder = Simple.new('Z',[])
elsif !item.children.nil?
holder = looper(item.children)
end
end
return holder
end
bar = looper(foo)
puts "Returned from looper: #{bar.name}"
In my actual code I ended up using the a class instance variable to get the response (which also works in the sample code). Example snippet of the function from above modified to the other pattern:
def looper(d)
holder = nil
d.each do |item|
# puts item.name
if item.name == 'D'
#holder = Simple.new('Z',[])
elsif !item.children.nil?
looper(item.children)
end
end
#holder
end
So my question is, is it good practice to use the instance variable? Any down sides to doing so since it works for my actual code whereas the first example pattern does not?
In your first piece of code, I'd expect to see a nil from your input, where in your second version you will get the object Simple.new('Z',[])
If that's your problem, it's because item g has children, the first one will set a value recusrively, but the second one will unset the value, so the second time through the loop, holder gets set to nil.
Edit: Actually my analysis of the results form the example above is wrong, because the last item in the top level list does contain the searched-for item. However, the analysis of the problem and difference in behaviour between the two solutions still holds in general.
You probably just want this:
def looper(d)
holder = nil
d.each do |item|
# puts item.name
if item.name == 'D'
holder = Simple.new('Z',[])
break
elsif !item.children.nil?
holder = looper(item.children)
break if holder
end
end
return holder
end
The break statement prevents you assigning again to holder . . .
Alternatively use Ruby internals like #first or #any? to express your search.
Assigning to instance variables as per your second example to inter-communicate between recursions is just fine, they are effectively shared state between all depths and iterations while the recursion runs. So it doesn't break recursion or Ruby per se, although you have to watch out for other kinds of unwanted interaction: For example you cannot assume an instance variable was set in a particular place during recursion, compared to current depth or loop item . . .
I would like to add some debugs for my simple ruby functions and I wrote a function as below,
def debug(&block)
varname = block.call.to_s
puts "#{varname} = #{eval(varname,block)}"
end
debug {:x} #prints x = 5
debug {:y} #prints y = 5
I understand that eval is evil. So I have two questions.
Is there any way to write that debug method without using eval? If NO is there a preferred way to do this?
Is there any way to pass a list of arguments to this method? I would ideally prefer debug {:x, :y. :anynumOfvariables}. I could not quite figure out how to factor that into the debug method (i.e, to take a list of arguments)
Just use arrays. You can use the Array method to ensure that you will always have an array, even if someone passes in only a single value:
def debug(&block)
Array(block[]).each do |var| puts "#{var} = #{eval var.to_s, block}" end
end
x, y = 3, 5
debug {:x} # => "x = 3"
debug {[:x, :y]} # => "x = 3" "y = 5"
BTW: passing a block as the binding no longer works in Ruby 1.9. (Despite the fact that the documentation says it does work.) You have to explicitly call Proc#binding to get a Binding object for that Proc:
def debug(&block)
Array(block.()).flatten.each do |var|
puts "#{var} = #{eval var.to_s, block.binding}"
end
end
Fortunately, this already works in Ruby 1.8, so you can futureproof your code by including it.
An alternative would be to forgo the block altogether. I mean, you already force the user of the debug to use the unfamiliar idiom of passing arguments in the block instead of in parentheses. Why not force them to just pass the binding instead?
def debug(*vars, bnd)
vars.each do |var|
puts "#{var} = #{eval var.to_s, bnd}"
end
end
x, y = 3, 5
debug :x, binding # => "x = 3"
debug :x, :y, binding # => "x = 3" "y = 5"
This has the added flexibility that they can actually pass a different binding than the one at the callsite, e.g. if they want to actually debug a piece of code in a different piece of the application.
BTW: here's some fun with Ruby 1.9.2's parameter introspection (Proc#parameters):
def debug(&block)
block.parameters.map(&:last).each do |var|
puts "#{var} = #{eval var.to_s, block.binding}"
end
end
x, y = 3, 5
debug {|x|} # => "x = 3"
debug {|x, y|} # => "x = 3" "y = 5"
And how I must use those methods to get names and values of variables from each loop, to place them as keys and values of nested hash?
The *attributes array is passed as an method's parameter.
I want to iterate through it, and every variable's name use as a key to nested hash and variable's value as value.
I got this:
def get_varname(&block)
varname = block.call.to_s
return varname
end
def create_instance_hash(env_attrs_obj, *attributes)
if not env_attrs_obj.has_key?("instances")
env_attrs_obj["instances"] = ""
end
attributes.each do |attr|
attr_name = get_varname{:attr}
env_attrs_obj["instances"][attr_name] = attr
end
end
instance_nat_ip_pub = "ip_pub_addr"
instance_nat_ip_prv = "ip_addr"
instance_nat_ami = "AMI_NAME"
instance_nat_aws_ssh_key_id = "AWS_SSH_KEY_ID"
instance_nat_id = "instance_id"
env_hash = {}
create_instance_hash(env_hash, *[instance_nat_ip_pub, instance_nat_ip_prv, instance_nat_ami, instance_nat_aws_ssh_key_id, instance_nat_id])
But:
def attrs_each_test(*attributes)
attributes.each do |attr|
puts get_varname{:attr}
end
end
And it outputs:
attr
attr
attr
attr
attr
The error when I run this create_instance_hash:
Line 12:in `[]=': string not matched (IndexError)
from t.rb:12:in `create_instance_hash'
from t.rb:10:in `each'
from t.rb:10:in `create_instance_hash'
from t.rb:24
How I can correct this error and achieve my goal?