I need to make a program in ruby to generate a robot name like KU765 or NG274 style and to store them and check it to avoid repetition. I also need to make a "reset" method to delete all stored names and start again. This program is not working for some reason. I hope somebody helps me to find the mistake. Thanks a lot.
class Robot
attr_accessor :named , :stored_names , :rl
def self.name
new.name
end
@@rl = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def name
named = ""
named << @@rl[rand(26).to_i]
named << @@rl[rand(26).to_i]
named << rand(100..999).to_s
named.save_name
named.check_name
end
def save_name
stored_names = []
stored_names << named
end
def check_name
stored_names.uniq!
end
def reset
stored_names = Array.new
end
end
Here's another way to construct the Robot
class that you may wish to consider. (My answers are not normally this long or detailed, but I wrote this in part to clarify aspects of Ruby's object model in my own mind. I hope it might help others do the same.)
Code
PREFACE = ('A'..'Z').to_a << ?_
SUFFIX = ('0'..'9').to_a
PREFACE_SIZE = 2
SUFFIX_SIZE = 3
class Robot
def self.reset() @bots = [] end
reset
def self.new() (@bots << super).last end
def self.bots() @bots end
def self.delete(bot) @bots.delete(bot) end
def self.bot_names() @bots.map { |b| b.name } end
attr_reader :name
def initialize() @name = add_name end
private
def add_name
loop do
@name = gen_name
return @name unless self.class.bot_names.include?(@name)
end
end
def gen_name
PREFACE.sample(PREFACE_SIZE).join << SUFFIX.sample(SUFFIX_SIZE).join
end
end
Example
Robot.bots #=> []
robbie = Robot.new #=> #<Robot:0x000001019f4988 @name="AP436">
robbie.name #=> "AP436"
Robot.bots #=> [#<Robot:0x000001019f4988 @name="AP436">]
r2d2 = Robot.new #=> #<Robot:0x000001019cd450 @name="KL628">
r2d2.name #=> "KL628"
Robot.bots #=> [#<Robot:0x000001019f4988 @name="AP436">,
# #<Robot:0x000001019cd450 @name="KL628">]
Robot.bot_names #=> ["AP436", "KL628"]
Robot.delete(robbie) #=> #<Robot:0x000001019f4988 @name="AP436">
Robot.bots #=> [#<Robot:0x000001019cd450 @name="KL628">]
Robot.bot_names #=> ["KL628"]
Robot.reset #=> []
c3po = Robot.new #=> #<Robot:0x000001018ff8c0 @name="VO975">
Robot.bots #=> [#<Robot:0x000001018ff8c0 @name="VO975">]
Explanation
When the class is parsed, the class method reset
is first created, then the line reset
is executed. As self => Robot
when that occurs, the class method reset
is executed, initializing @bots
to an empty array.
The responsibility for saving and modifying a list of instances of Robot
lies with the class. This list is held in the class instance variable @bots
.
Instance of Robot
are created by invoking Robot::new
, which allocates memory and then invokes the (private) instance method initialize
. Where is new
? Since we have not defined it as a class method in Robot
, there are two possibilities: it is inherited from one of Robot
's ancestors (Robot.ancestors => [Robot, Object, Kernel, BasicObject]
) or it is an instance method of the class Class
, as that is the class for which Robot
is an instance (i.e., Robot.class => Class
) Let's find out which: Class.instance_method(:new) => #<UnboundMethod: Class#new>
(or Class.instance_methods.include?(:new) => true
), Object.method(:new) => #<Method: Class#new>
. It's both! But that makes sense, because all classes are instances of Class
, including Robot
's superclass, Object
. #<Method: Class#new>
returned by Object.method(:new)
shows new
is defined in Class
(which can alternatively be seen with Robot.method(:new).owner => Class
. Very cool, eh? If you didn't know this already, and can follow what I've said in this paragraph, you've just learned the essence of Ruby's object model!
Suppose we add the class method new
, shown below, to Robot
. super
invokes the class method Object::new
(which is the instance method Class#new
), passing any arguments of new
(here there aren't any). Object::new
returns the instance that it creates, which Robot::new
in turn returns. Therefore, this method would simply be a conduit and and have no effect on the results.
def self.new
super
end
We can make a small change to the above method to add a copy of the instance that is created by Object::new
to the array @bots
:
def self.new
instance = super
@bots << instance
instance
end
I have written this a little more compactly as:
def self.new
(@bots << super).last
end
I've used the method Array#sample to randomly draw PREFACE_SIZE
characters from PREFACE
and SUFFIX_SIZE
characters from SUFFIX_SIZE
. sample
samples without replacement, so you will not get, for example, "UU112". If you want to sample with replacement, replace the method gen_name
with the following:
def gen_name
str = PREFACE_SIZE.times.with_object('') { |_,s| s << PREFACE.sample }
SUFFIX_SIZE.times { str << SUFFIX.sample }
str
end
I have created a class method bots
to return the value of the class instance variable @bots
. This could alternatively be done by defining a read accessor for @bots
on Robots
' singleton class:
class << self
attr_reader :name
end
When Robot.reset
is invoked, what happens to all the instances of Robot
it had contained? Will they be left to wander the forest, rejected and homeless? In languages like C
you need to release their memory before casting them aside. In Ruby and many other modern languages that's not necessary (and generally can't be done). Ruby's "garbage collection" keeps track of all objects, and kills off (after releasing memory) any that are no longer referenced by any other object. Nice, eh?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With