4

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
0

5 Answers 5

3

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?

Sign up to request clarification or add additional context in comments.

4 Comments

This is not an answer. It's a blog. +1
I have a gem to automate this sort of tasks around naming objects and keeping record of the instances, it's y_support/name_magic. But you have an inspiring way of writing one-liner methods.
And #sample takes an argument... Time to hit the docs again.
Thanks, @BorisStitnicky. That' a cool and useful gem. One form of Array#sample takes no argument. There's even an example in the docs: a.sample #=> 7. It's been that way since at least 1.9.3.
2
def save_name
  stored_names = []
  stored_names << named
end

Every time, you create a name, and call save_name you delete all previously created names, by assigning an empty array to stored_names

EDIT: There were a few more errors, let me first post a working solution:

class Robot
  attr_accessor :named , :stored_names , :rl

  def initialize()
    @stored_names = []
  end

  @@rl = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars.to_a

  def name
    @named = ""
    @named << @@rl.sample
    @named << @@rl.sample
    @named << rand(100..999).to_s
    save_name 
    check_name
  end

  def save_name
    @stored_names << @named
  end

  def check_name
    @stored_names.uniq!
  end

  def reset
    @stored_names = Array.new
  end
end
  • To access the members of your object, you have to prefix them with @.

  • You called save_name and check_name on @named, which is a string and doesn't provide these methods

  • @stored_names must be initialized to an empty array, before you can push elements into it with <<. This is normally done in the class's constructor initialize()

3 Comments

yes, it is true, nice one, but still not working properly. It is something about how methods has been delcared.
You know that instead of @@rl[rand(26).to_i] you can use Array#sample or if _ isn't needed then rand('A'..'Z')?
Nice catch, I didn't knew about Array#sample, I'll edit it, since it really does make the code more readable
2

The task itself is not hard, but I don't like the way your code is organised. This is what I would do in the first stage:

class Robot
  class Name < String
    class << self
      def sign
        "#{[*?A..?Z].sample}#{[*?A..?Z].sample}"
      end

      def number
        "#{rand 1..9}#{rand 0..9}#{rand 0..9}"
      end

      def new
        super << sign << number
      end
    end
  end
end

And then:

Robot::Name.new

When constructing a list of names it is easy to check that they are unique. This is how I'd go about it:

class Robot
  class Names < Array
    def self.generate n
      new.tap { |array| n.times do array.add_random_name end }
    end

    def add_random_name
      name = Name.new
      include?( name ) ? add_random_name : self << name
    end
  end
end

And then:

Robot::Names.generate 7

6 Comments

Awesome response. Thanks a lot. But I am a beginner and your way of doing it is not for me for the moment. Hopefully one day...
looks really awesome and laconically
@RadW2020 Don't say that to yourself. This is not an advanced stuff. Just the books don't teach you to begin using classes and namespaces like a human being from the get go.
A detail: in place of rand( ?A.ord .. ?Z.ord ).chr consider P.sample, after defining P = (?A..?Z).to_a.
Why not Robot is declared as a module ?
|
1

I understand this isn't efficient, but this will work.

letters = [*'A'..'Z'] => 
numbers = [*100..999]
names = []
2.times{names.push(letters.shuffle.first)} => Randomizing array and choosing element
names.push(numbers.shuffle.first)
names.join => Creates a String object out of the array elements

This isn't pretty, but it gets the job done.

Comments

1

This is how I automate Cary's approach with my y_support/name_magic:

require 'y_support/all'

class Robot
  ★ NameMagic

  def name_yourself
    begin
      self.name = "#{[*?A..?Z].sample( 2 ).join}#{rand 100..999}"
    rescue NameError; retry end
  end
end

3.times { Robot.new.name_yourself }
Robot.instances #=> [PR489, NS761, OE663]
Robot.forget "PR489"
Robot.instances #=> [NS761, OE663]
Robot.forget_all_instances
Robot.instances #=> []
Robot.new.name_yourself
Robot.instances #=> [IB573]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.