I have a class that has hashes in various stages of "completion". This is to optimize so that I don't have to keep recreating hashes with root data that I already know. For example this is a counter called #root that would serve as a starting point.
{3=>4, 4=>1, 10=>3, 12=>5, 17=>1}
and it took key+key+key+key+key number of iterations to create #root. But now I have all combinations of [x,y] left to be added to the counter and individually evaluated. So I could do it like:
a = (1..52)
a.combination{|x,y|
evaluate(x,y)
}
But instead of I would like to do this:
a.each{|x|
evaluate(x, "foo")
a.each {|y| evaluate(y, "bar")}
}
Where i have a method like this to keep track of the hash at each state:
def evaluate index, hsh
case hsh
when "root"
#root.key?(index) ? #root[index] += 1 : #root[index] = 1
when "foo"
#foo = #root.clone
#foo.key?(index) ? #foo[index] += 1 : #foo[index] = 1
when "bar"
#bar = #foo.clone
#bar.key?(index) ? #bar[index] += 1 : #bar[index] = 1
end
end
But there is alot of repetition in this method. Is there a way that I could do this dynamically without using eval?
Instead of using hsh as a string descriptor, you can directly pass the hash object as parameter to your method evaluate? E.g. instead of evaluate(x, "foo") you write
#foo = #root.clone
evaluate(x, #foo)
Also note the #root.clone in your code overwrites the field several times inside the loop.
Additionally if you use a default initializer for your hash you save quite some logic in your code. E.g. the code lines
h = Hash.new{0}
...
h[index] += 1
will set the default value to zero if non was set for index. Thus you do not have to take care of the special case inside your evaluate method.
Related
I would like to be able to do this:
my_array = Array.new
my_array[12] += 1
In other words, somehow upon trying to access entry 12, finding it uninitialized, it is initialized to zero so I can add one to it. Array.new has a default: parameter, but that comes into play when you initialize the array with a known number of slots. Other than writing my own class, is there a ruby-ish way of doing this?
No need to create a new class :
my_hash = Hash.new(0)
my_hash[12] += 1
p my_hash
#=> {12=>1}
For many cases, hashes and arrays can be used interchangeably.
An array with an arbitrary number of elements and a default value sounds like a hash to me ;)
Just to make it clear : Hash and Array aren't equivalent. There will be cases where using a hash instead of an array will be completely wrong.
Something like:
a[12] = (a[12] ||= 0) + 1
Making use of nil.to_i == 0
my_array = Array.new
my_array[12] = my_array[12].to_i + 1
Note, that unlike other solutions here so far, this one works for any arbitrary initial value.
my_array = Array.new.extend(Module.new {
def [] idx
super || 0
end
})
my_array[12] += 1
#⇒ 1
This is not possible with the stock Array::new method.
https://docs.ruby-lang.org/en/2.0.0/Array.html#method-c-new
You will either need to monkey patch Array class, or monkey patch nil class. And they are not recommended.
If you have a specific use case, I would create a new wrapper class around Array
class MyArray < Array
def [](i)
super(i) ? super(i) : self[i] = 0
end
end
arr = MyArray.new
arr[12] += 1 # => 1
I cannot understand this ruby behavior, the code explains better what I mean:
class DoNotUnderstand
def initialize
#tiny_array = [3,4]
test
end
def messing(ary)
return [ary[0]+=700, ary[1]+=999]
end
def test
puts #tiny_array.join(",") # before => 3,4
messing(#tiny_array)
puts #tiny_array.join(",") # after => 703,1003
end
end
question = DoNotUnderstand.new
#tiny_array was [3,4] and became [703,1003]
if I don't use a class, that happens:
#tiny = [1,2]
def messing(ary)
return [ary[0]+693,ary[1]+999]
end
puts #tiny.join(",") # before => 1,2
messing(#tiny)
puts #tiny.join(",") # after => 1,2
the array simply remains [1,2]
why?
The class is a red herring, and completely irrelevant to the issue.
In the first case, where the array was modified, you defined messing as:
def messing(ary)
return [ary[0]+=700, ary[1]+=999]
end
Whereas in the second case, where the array was not modified, you defined messing as:
def messing(ary)
return [ary[0]+693,ary[1]+999]
end
In one case you used +=, and in the other, you used merely +.
ary[0] += 700 is exactly equivalent to ary[0] = ary[0] + 700. In other words you are changing the value stored in the 0th index of ary.
In the second case you merely add to the values stored in the array and return the result, but in the first case you not only return the result, you also store it back in the array.
For an explanation of why modifying ary modifies #tiny_array, see this answer to the question Is Ruby pass by reference or by value?.
You're second code example (the one from outside the class) is missing the two characters in the first that make it work the way it does. In the first example, the += operator is used, modifying the array in place:
return [ary[0]+=700, ary[1]+=999]
In your second example, the + operator is used, leaving the array as is:
return [ary[0]+693,ary[1]+999]
If you change it use the += operator, it works the same way as the first code snippet.
I'm getting familiar with ruby send method, but for some reason, I can't do something like this
a = 4
a.send(:+=, 1)
For some reason this doesn't work. Then I tried something like
a.send(:=, a.send(:+, 1))
But this doesn't work too. What is the proper way to fire plus equals through 'send'?
I think the basic option is only:
a = a.send(:+, 1)
That is because send is for messages to objects. Assignment modifies a variable, not an object.
It is possible to assign direct to variables with some meta-programming, but the code is convoluted, so far the best I can find is:
a = 1
var_name = :a
eval "#{var_name} = #{var_name}.send(:+, 1)"
puts a # 2
Or using instance variables:
#a = 2
var_name = :#a
instance_variable_set( var_name, instance_variable_get( var_name ).send(:+, 1) )
puts #a # 3
See the below :
p 4.respond_to?(:"+=") # false
p 4.respond_to?(:"=") # false
p 4.respond_to?(:"+") # true
a+=1 is syntactic sugar of a = a+1. But there is no direct method +=. = is an assignment operator,not the method as well. On the other hand Object#send takes method name as its argument. Thus your code will not work,the way you are looking for.
It is because Ruby doesn't have = method. In Ruby = don't work like in C/C++ but it rather assign new object reference to variable, not assign new value to variable.
You can't call a method on a, because a is not an object, it's a variable, and variables aren't objects in Ruby. You are calling a method on 4, but 4 is not the thing you want to modify, a is. It's just not possible.
Note: it is certainly possible to define a method named = or += and call it, but of course those methods will only exist on objects, not variables.
class Fixnum
define_method(:'+=') do |n| self + n end
end
a = 4
a.send(:'+=', 1)
# => 5
a
# => 4
This might miss the mark a bit, but I was trying to do this where a is actually a method dynamically called on an object. For example, with attributes like added_count and updated_count for Importer I wrote the following
class Importer
attr_accessor :added_count, :updated_count
def increment(method)
send("#{method}=", (send(method) + 1))
end
end
So I could use importer.increment(:added_count) or importer.increment(:updated_count)
Now this may seem silly if you only have these 2 different counters but in some cases we have a half dozen or more counters and different conditions on which attr to increment so it can be handy.
My background is in PHP and C#, but I'd really like to learn RoR. To that end, I've started reading the official documentation. I have some questions about some code examples.
The first is with iterators:
class Array
def inject(n)
each { |value| n = yield(n, value) }
n
end
def sum
inject(0) { |n, value| n + value }
end
def product
inject(1) { |n, value| n * value }
end
end
I understand that yield means "execute the associated block here." What's throwing me is the |value| n = part of the each. The other blocks make more sense to me as they seem to mimic C# style lambdas:
public int sum(int n, int value)
{
return Inject((n, value) => n + value);
}
But the first example is confusing to me.
The other is with symbols. When would I want to use them? And why can't I do something like:
class Example
attr_reader #member
# more code
end
In the inject or reduce method, n represents an accumulated value; this means the result of every iteration is accumulated in the n variable. This could be, as is in your example, the sum or product of the elements in the array.
yield returns the result of the block, which is stored in n and used in the next iterations. This is what makes the result "cumulative."
a = [ 1, 2, 3 ]
a.sum # inject(0) { |n, v| n + v }
# n == 0; n = 0 + 1
# n == 1; n = 1 + 2
# n == 3; n = 3 + 3
=> 6
Also, to compute the sum you could also have written a.reduce :+. This works for any binary operation. If your method is named symbol, writing a.reduce :symbol is the same as writing a.reduce { |n, v| n.symbol v }.
attr and company are actually methods. Under the hood, they dynamically define the methods for you. It uses the symbol you passed to work out the names of the instance variable and the methods. :member results in the #member instance variable and the member and member = methods.
The reason you can't write attr_reader #member is because #member isn't an object in itself, nor can it be converted to a symbol; it actually tells ruby to fetch the value of the instance variable #member of the self object, which, at class scope, is the class itself.
To illustrate:
class Example
#member = :member
attr_accessor #member
end
e = Example.new
e.member = :value
e.member
=> :value
Remember that accessing unset instance variables yields nil, and since the attr method family accepts only symbols, you get: TypeError: nil is not a symbol.
Regarding Symbol usage, you can sort of use them like strings. They make excellent hash keys because equal symbols always refer to the same object, unlike strings.
:a.object_id == :a.object_id
=> true
'a'.object_id == 'a'.object_id
=> false
They're also commonly used to refer to method names, and can actually be converted to Procs, which can be passed to methods. This is what allows us to write things like array.map &:to_s.
Check out this article for more interpretations of the symbol.
For the definition of inject, you're basically setting up chained blocks. Specifically, the variable n in {|value| n = yield(n, value)} is essentially an accumulator for the block passed to inject. So, for example, for the definition of product, inject(1) {|value| n * value}, let's assume you have an array my_array = [1, 2, 3, 4]. When you call my_array.product, you start by calling inject with n = 1. each yields to the block defined in inject, which in turns yields to the block passed to inject itself with n (1) and the first value in the array (1 as well, in this case). This block, {|n, value| n * value} returns 1 == 1 * 1, which is set it inject's n variable. Next, 2 is yielded from each, and the block defined in inject block yields as yield(1, 2), which returns 2 and assigns it to n. Next 3 is yielded from each, the block yields the values (2, 3) and returns 6, which is stored in n for the next value, and so forth. Essentially, tracking the overall value agnostic of the calculation being performed in the specialised routines (sum and product) allows for generalization. Without that, you'd have to declare e.g.
def sum
n = 0
each {|val| n += val}
end
def product
n = 1
each {|val| n *= val}
end
which is annoyingly repetitive.
For your second question, attr_reader and its family are themselves methods that are defining the appropriate accessor routines using define_method internally, in a process called metaprogramming; they are not language statements, but just plain old methods. These functions expect to passed a symbol (or, perhaps, a string) that gives the name of the accessors you're creating. You could, in theory, use instance variables such as #member here, though it would be the value to which #member points that would be passed in and used in define_method. For an example of how these are implemented, this page shows some examples of attr_* methods.
def inject(accumulator)
each { |value| accumulator = yield(accumulator, value) }
accumulator
end
This is just yielding the current value of accumulator and the array item to inject's block and then storing the result back into accumulator again.
class Example
attr_reader #member
end
attr_reader is just a method whose argument is the name of the accessor you want to setup. So, in a contrived way you could do
class Example
#ivar_name = 'foo'
attr_reader #ivar_name
end
to create an getter method called foo
Your confusion with the first example may be due to your reading |value| n as a single expression, but it isn't.
This reformatted version might be clearer to you:
def inject(n)
each do |value|
n = yield(n, value)
end
return n
end
value is an element in the array, and it is yielded with n to whatever block is passed to inject, the result of which is set to n. If that's not clear, read up on the each method, which takes a block and yields each item in the array to it. Then it should be clearer how the accumulation works.
attr_reader is less weird when you consider that it is a method for generating accessor methods. It's not an accessor in itself. It doesn't need to deal with the #member variable's value, just its name. :member is just the interned version of the string 'member', which is the name of the variable.
You can think of symbols as lighter weight strings, with the additional bonus that every equal label is the same object - :foo.object_id == :foo.object_id, whereas 'foo'.object_id != 'foo'.object_id, because each 'foo' is a new object. You can try that for yourself in irb. Think of them as labels, or primitive strings. They're surprisingly useful and come up a lot, e.g. for metaprogramming or as keys in hashes. As pointed out elsewhere, calling object.send :foo is the same as calling object.foo
It's probably worth reading some early chapters from the 'pickaxe' book to learn some more ruby, it will help you understand and appreciate the extra stuff rails adds.
First you need to understand where to use symbols and where its not..
Symbol is especially used to represent something. Ex: :name, :age like that. Here we are not going to perform any operations using this.
String are used only for data processing. Ex: 'a = name'. Here I gonna use this variable 'a' further for other string operations in ruby.
Moreover, symbol is more memory efficient than strings and it is immutable. That's why ruby developer's prefers symbols than string.
You can even use inject method to calculate sum as (1..5).to_a.inject(:+)
I know a bit about ruby way to handle objects and references. The replace stuff, ect ...
I know it d'ont work on fixnum, cause the var is the fixnum. But i wish to change the value of a fixnum inside a function, and that the value changed in the ouside var.
How can i do this ?
I guess i can use a string like this "1" but that's quite dirty.
Ruby will always pass-by-reference (because everything is an object) but Fixnum lacks any methods that allow you to mutate the value. See "void foo(int &x) -> Ruby? Passing integers by reference?" for more details.
You can either return a value that you then assign to your variable, like so:
a = 5
def do_something(value)
return 1 #this could be more complicated and depend on the value passed in
end
a = do_something(a)
or you could wrap your value in an object such as a Hash and have it updated that way.
a = {:value => 5}
def do_something(dict)
dict[:value] = 1
end
do_something(a) #now a[:value] is 1 outside the function
Hope this helps.
You could pass an array with a single number, like [1], or a hash like {value: 1}. Less ugly than a string, as your number itself remains a number, but less overhead than a new class...
When I was building a game I had the same problem you have. There was a numeric score that represented how many zombies you've killed and I needed to manually keep it in sync between Player (that incremented the score), ScoreBar and ScoreScreen (that displayed the score). The solution I've found was creating a separate class for the score that will wrap the value and mutate it:
class Score
def initialize(value = 0)
#value = value
end
def increment
#value += 1
end
def to_i
#value
end
def to_s
#value.to_s
end
end