Calculating a map of instance variables - ruby

My module method should return a Hash, where the keys are symbols denoting the instance variables of self and the Hash values are their respective values. I came up with the following solution:
module M
def self.set
#foo = 5
end
def self.ivar_map
Hash[instance_variables.map{|iv| [iv, instance_variable_get(iv)]}]
end
end
M.set
M.ivar_map # => {:#foo=>5}
This works of course, but I wonder whether there isn't a simpler way to achieve the same result. After all, Ruby somehow has to maintain such a symbol table already, mapping the variables to their content.

This works of course, but I wonder whether there isn't a simpler way to achieve the same result.
There are some slight simplifications that you could do:
Use Array#to_h instead of Hash::[].
Use Array#zip.
Use point-free style using Object#method or something like the method reference operator if that is ever added to Ruby.
module M
def self.ivar_map
instance_variables.zip(instance_variables.map(&method(:instance_variable_get))).to_h
end
end
or with the experimental (and removed again in 2.7.0-preview3) method reference operator:
module M
def self.ivar_map
instance_variables.zip(instance_variables.map(&self.:instance_variable_get)).to_h
end
end
After all, Ruby somehow has to maintain such a symbol table already, mapping the variables to their content.
Actually, that is not true. Several Ruby Implementations (including YARV, the most widely-used one) optimize instance variables to some extent. E.g. I believe YARV stores up to three instance variables in the object header directly, without using an instance variable table.
And YARV is on the more simple side when it comes to optimizations. Other implementations like Rubinius, JRuby, TruffleRuby, and RubyOMR perform much more sophisticated optimizations.

Related

What strategy do I use to avoid global variables in ruby?

I'm learning Ruby through 'Learn to Program' by Chris Pine. I'm trying to cope without global variables. I have a sloppy looking command prompt blackjack program (can't split yet) My first three methods illustrate the problem I'm having. In Pascal I would have used global variables, and used methods to manipulate those global variables. It would look something like this (using ruby pseudo language)
$deck_of_cards = []
$player_hand = []
$dealer_hand = []
def create_deck
$deck_of_cards = #numbers 1-52
end
def shuffle_deck
$deck_of_cards = #shuffled up deck of cards
end
def opening_deal
2.times do
$player_hand.push $deck_of_cards.pop
$dealer_hand.push $deck_of_cards.pop
end
end
create_deck
shuffle_deck
opening_deal
and I would end up with a shuffled deck, a player hand, and a dealer hand.
Taking away global variables, the first two methods now look like this:
def create_deck deck_of_cards
deck_of_cards = #numbers 1-52
return deck_of_cards
end
def shuffle_deck deck_of_cards
deck_of_cards = #shuffled up deck of cards
return deck_of_cards
end
deck = create_deck([])
deck = shuffle_deck(deck)
which feels a little odd but I could get used to it.
But I'm completely stuck on the last method, which would need to return two variables, player_hand and dealer_hand. I could push those two arrays into another array and return that, but that doesn't seem like it's simplifying anything.
Is there a general strategy for dealing with this, or does each situation have its own unique solution?
Another situation that comes to mind is a chess program. Why would it not make sense to make the chess board a global variable since almost all methods in a chess program would need to use it's value?
The short answer: Objects. Object oriented programming allows you to encapsulate data so that it doesn't pollute the global namespace. As your projects grow larger, it becomes unmaintainable to have all your data in the global namespace. Objects allow you to group data and methods into meaningful units, which can then be re-used and maintained more easily.
So for instance with the example you provided we could make deck and hand objects. This then allows us to create multiple deck and hands easily because these objects encapsulate and manage their data.
As a rough outline of how you might organize your classes / methods:
class Deck
def shuffle
end
def draw_hand
end
end
class Hand
end
class Card
end
In general, using object oriented programming techniques you would define classes and create objects of those classes and then pass around those objects. You can think of objects as sort of like arrays in that they can contain lots of related data, but better than arrays in that the data elements can have names and can be any type and do not need to be the same type and they come with named functions for operating on the data. (Actually, in Ruby, Array elements can be any type and do not need to be the same type, and the same is true of Hash elements plus Hash elements have names, but Ruby is special that way. Few other languages allow that.)
Keeping it simple, though, you can continue your program like this:
def opening_deal deck_of_cards
player_hand = []
dealer_hand = []
2.times do
player_hand.push deck_of_cards.pop
dealer_hand.push deck_of_cards.pop
end
return player_hand, dealer_hand
end
player_hand, dealer_hand = opening_deal deck
It is a bad habit to have global variables, but it is not bad to have global constants, or in other words, constants defined in the main name space. When in need of a global variable, you can always define a constant in the main name space. Mutable objects like string, hash, array can be modified even if it is a constant. If that is not enough and you need more complicated things, then you can define a module (which is a special case of constants), and define methods on that module to have access to the things you want to do, and encapsulate the complicated information inside that module using instance variables.

Ruby's 'is_a?' is returning false for built-in types, what's happening?

I've only been learning the deep parts of Ruby for a few months so apologies if this is a bit of a dumb question. I'm attempting to recursively iterate through an Array that may contain nested Arrays and as such I need to check the type of the current element. I have the following code as a small test:
arr = [ 1..2, [3..4, [5..6]], [7..8, [9..10]] ]
arr.each do |node|
p node.class
p node.instance_of? Array
end
When I run it, I get the following output:
Range
false
Array
false
Array
false
I expected the last two to return True, given I have an Array containing a Range and two nested Arrays.
What's even weirder, is if I write the following:
node.class.name == "Array"
It returns True, as it should.
What's happening here?
Ruby Version: MRI 1.9.3-p194
Note: I eventually realised that this was occurring due to the way I namespace my code using modules to avoid code-collision, like so, but also verify object identity in a naughty way:
module MyProg
class MyClass
attr_reader :my_array
def initialize(size)
#my_array = Array.new(size)
end
end
end
MyProg::MyClass.new
Doing this isolates your code but has the downfall of causing all class lookups to be resolved starting from under your namespace. This means that in the above example, my_array.class would actually resolve to MyProg::Array instead of the global Array class.
If you namespace like this and you still want to use this method, you can remedy it by using the double-colon global identifier before the class to force Ruby to begin lookup from the global namespace:
arr.is_a? ::Array
arr.is_a? ::String
Given Ruby's Duck Typing abilities however (and for better code maintenance later on), you should really be testing the behaviour of the object as-per Peter's suggestion below. As such I'm marking his answer as correct for some excellent help given to a learner!
I wrote another answer, but one major question is - why are you doing this? Why not, instead, just call flatten on the array so you just get the entries? Or, check for the behavior of the objects instead. You might need to give more detail about what you require.
You really mean is_a?, which is a more general test to see if the node is of type Array, rather than a specific instance of the specific Array class that you mention. See here for more details. But if you just use is_a? everything will make sense.
I ran your code and got these results.
Range
false
Array
true
Array
true
I'm running ruby 1.9.3p125

Enumerable::each_with_index now optionally takes a arguments in Ruby 1.9. What significance and/or what is a use case for that?

In Ruby 1.8.7 and prior, Enumerable::each_with_index did not accept any arguments. In Ruby 1.9, it will accept an arbitrary number of arguments. Documentation/code shows that it simply passes those arguments along to ::each. With the built in and standard library Enumerables, I believe passing an argument will yield an error, since the Enumerable's ::each method isn't expecting parameters.
So I would guess this is only useful in creating your own Enumerable in which you do create an ::each method that accepts arguments. What is an example where this would be useful?
Are there any other non-obvious consequences of this change?
I went through some gems code and found almost no uses of that feature. One that it does, spreadsheet:
def each skip=dimensions[0], &block
skip.upto(dimensions[1] - 1) do |idx|
block.call row(idx)
end
end
I don't really see that as an important change: #each is the base method for classes that mix-in module Enumerable, and methods added (map, select, ...) do not accept arguments.

How can I splattify an anonymous object so I can use &method on it?

I'm wanting to use the &method(:method_name) idiom when there's more than one object required by method_name. Can I do this under Ruby 1.9?
For example, if I've got
def move_file(old_filename, new_filename)
STDERR.puts "Moving #{old_filename.inspect} to #{new_filename.inspect}"
# Implementation for careful moving goes here
end
old_filenames = ["foo.txt", "bar.txt", "hoge.ja.txt"]
new_filenames = ["foo_20110915.txt", "bar_20110915.txt", "hoge_20110915.ja.txt"]
the code
old_filenames.zip(new_filenames).each(&method(:move_file))
works under Ruby 1.8, but not under Ruby 1.9. Under Ruby 1.9, it's trying to do move_file(["foo.txt", "foo_20110915.txt"]) instead of move_file("foo.txt", "foo_20110915.txt").
How do I splattify it so it has the correct arity?
Workarounds I'm aware of:
Replace def move_file(old_filename, new_filename) with def move_file(*arguments)
Replace each(&method(:move_file)) with
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
Instead
each{|old_filename, new_filename| move_file(old_filename, new_filename)}
you should be able to do
each{|pair| move_file(*pair)}
But I don't know how you'd pull off blockless variant (I needed it couple of times as well). I guess &-shorthand was made to make the syntax simpler, and is not meant to be clogged much (whether it will be passed an array as an array, or splatted, for example). :)
How do I splattify it so it has the correct arity?
I don't think there is a way to do this while being compatible to both Ruby versions. What you could do is wrap it into a lambda
move_from_to = Proc.new {|*both| move_files(*both) }
The thing is - block and proc arity is something that got addressed in Ruby 1.9 so there might be a difference in behavior there. Also see prc.lambda? here http://www.ruby-doc.org/core/classes/Proc.html for info on what it does to the arity.
This question is also related to what you want to do (the solution there is to resplat and unsplat manually): Inconsistency of arity between Hash.each and lambdas

Array of Types in Ruby

I am trying to create instances of objects of various types by iterating and checking for validity. I need an array of types so I can do something like this:
def tryClasses(in)
types = [Foo::A, Foo::B, Foo::C]
types.each do |type|
a = type.new(in)
return a != null
end
end
How do I create and array of class types?
Doing it this way I am getting a NoMethodError (undefined method 'A' for Foo)
Apart from the obvious syntactic errors (e.g. in is a reseved word, and null is spelled nil in Ruby), the code you showed should work just fine as it is, and indeed it does when I copy&paste it into my Ruby installation. This assumes, of course, that the classes Foo::A, Foo::B and Foo::C actually exist. If they don't, then the code obviously cannot possibly work.
It is, however, completely un-Rubyish and violates just about every coding convention in the book:
indentation is 2 spaces
method names are snake_case, not camelCase
explicitly checking for equality to nil is a no-no, simply calling #nil? is much preferred
try_classes isn't exactly an intention-revealing method name
and WTF does in mean?
Rubyists much prefer higher-order methods over explicit looping
Here's a more Rubyish version of the code you wrote:
def can_create_object?(*args)
[Foo::A, Foo::B, Foo::C].none? do |klass|
klass.new(*args).nil?
end
end
However, note that I am pretty convinced that the whole idea is fundamentally flawed.

Resources