When I run the command I get
all the song names and then the instances following it. How do I only
get the names?
class Song
##all = []
attr_accessor :name
def initialize(name)
#name = name
##all << self
end
def self.all
##all
end
def self.print_all_song_names
##all.each do |song|
puts song.name
end
end
end
hotline_bling = Song.new("Hotline Bling")
thriller = Song.new("Thriller")
ninety_nine_problems = Song.new("99 Problems")
thriller = Song.new("Thriller")
puts Song.print_all_song_names
Which outputs:
Hotline Bling
Thriller
99 Problems
Thriller
#<Song:0x00000000058ced30>
#<Song:0x00000000058cecb8>
#<Song:0x00000000058cebc8>
#<Song:0x00000000058ceb50>
The issue with your code is your calling puts here and there.
The code already calls puts in print_all_song_names, and after you call puts Song.print_all_song_names which roughly means call the method and print the value returned.
each returns a receiver, which means print_all_song_names returns the value of ##all class variable. Which gets printed again.
To fix it, just don’t call puts in the last line; Song.print_all_song_names already prints out everything needed.
Song.print_all_song_names # voilà
Related
Okay, this is a little hard to explain but I will try (For starters I am only just learning to code so it may be something super simple I'm missing..)
I created a few classes, I put a name in those classes, I put them in an array, I then chose one at random and try to puts the name, and it outputs blank.
Am I doing this all completely wrong? I've been learning ruby for about 3 months now so I'm sure there is a lot I don't know.
class A
attr :name
def set_name
#name = "Aaa"
end
def get_name
return #name
end
end
class B
attr :name
def set_name
#name = "Bbb"
end
def get_name
return #name
end
end
class C
attr :name
def set_name
#name = "Ccc"
end
def get_name
return #name
end
end
name_a = A.new
name_b = B.new
name_c = C.new
which_name = Array.new
which_name[0] = name_a
which_name[1] = name_b
which_name[2] = name_c
roll = rand(max 3)
puts which_name[roll].get_name
I then chose one at random and try to puts the name, and it outputs
blank.
You never called the #set_name method in your code. You can add this:
name_a.set_name
name_b.set_name
name_c.set_name
Also, you probably want to look into #attr_accessor.
Running this code ...
class List
attr_reader :all_tasks
def initialize
#all_tasks = []
end
def add(task)
all_tasks.push(task)
end
def show
all_tasks
end
end
class Task
attr_reader :description
def initialize(description)
#description = description
end
end
if __FILE__ == $PROGRAM_NAME
my_list = List.new
my_list.add(Task.new("FIRST"))
puts my_list.all_tasks
my_list.add(Task.new("SECOND"))
my_list.add(Task.new("THIRD"))
puts "Next line is my_list.show execution:"
puts my_list.show
end
... I get the following Terminal output:
#<Task:0x007fb24b08fed0>
Next line is my_list.show execution:
#<Task:0x007fb24b08fed0>
#<Task:0x007fb24b08fdb8>
#<Task:0x007fb24b08fd68>
My question is, why am I not seeing the values of the array stored in my_list? (If I understand programming vernacular correctly, the output of Terminal is the object itself, not the values stored in the object. Am I understanding that right?)
#<Task:0x007fb24b08fed0>
means an instance of class Task, or in other words, a Task object.
It has id 0x007fb24b08fed0.
This is the standard Ruby representation for objects that don't answer to to_s method.
You could just define Task#to_s, which is called by puts :
class Task
def to_s
#description
end
end
The output becomes :
FIRST
Next line is my_list.show execution:
FIRST
SECOND
THIRD
Note : If you call Task#inspect, you see the class, the object id and the defined instance variables.
puts task.inspect
#<Task:0x00000002657af0 #description="FIRST">
It is doing exactly what you are telling it to do, which is show. Your show method returns the array of tasks, hence an array of objects. Modify your show method as such
def show
all_tasks.each do |task|
puts task.description
end
end
This will return each task description.
Just change the last line from puts my_list.show to my_list.show, as the puts is already in the show method.
I'm not sure why my code will not pass the corresponding test. Each time I try the code, the following error is reported: "rb:60:in <main>': undefined local variable or methodtrack' for main:Object (NameError)." What can I do without editing the tests? Thanks! Open to another approach...thanks!
strong text
class Song
attr_reader :song
def initialize(song, artist)
#song = song
#artist = artist
end
def play
puts "#{#song}by #{#artist}"
end
end
class Playlist
def initialize(player_list)
#player_list = player_list
end
def add(add_song)
add_song.each do |song|
#player_list << song
end
end
def track_number
#player_list.length
end
def remove(remove_song)
remove_song.each do |song|
#player_list.delete(song)
end
end
def includes?(from_list)
i = 0
from_list.each do |song|
if #player_list.include?(song)
i+=1
end
end
if i==from_list.length
true
else
false
end
end
def play_all
#player_list.each do |song|
song.play
end
end
def display
#player_list.each do |song|
puts song.song
end
end
end
one_by_one = Song.new("One by One", "Sirenia")
world_so_cold = Song.new("World So Cold", "Three Days Grace")
going_under = Song.new("Going Under", "Evanescence")
my_playlist = Playlist.new(one_by_one, world_so_cold, going_under)
lying_from_you = Song.new("Lying From You", "Linkin Park")
angels = Song.new("Angels", "Within Temptation")
my_playlist.add(lying_from_you, angels)
p my_playlist.track_number == 5
going_under.play
my_playlist.remove(angels)
p my_playlist.includes?(lying_from_you) == true
my_playlist.play_all
my_playlist.display
When you call the "add" method for PlayList, it is expecting a single Song. However, the "add" method is trying to call .each() which would work for an array of Songs but not for a single song.
The best solution, without changing your test code, would be to remove the ".each" calls in the "add", "remove" and "includes?" methods for PlayList. Have them each add, remove or check for a single song at a time.
class Books
attr_accessor :name, :book_id
def initialize(name, book_id)
#name = name,
#book_id = book_id
end
end
class BookCollection
def intialize
#book_names = []
end
def add_to_books(book_name)
book_name.push(book_names)
end
end
book1 = Books.new("catch22", "12345")
book_collection1 = BookCollection.new
book_collection1.add_to_books(book1.name)
puts book_collection1
end
That is my code and the error I'm getting is "undefined local variable or method `book_names'". I tried adding " attr_accessor :book_names" and when I do that the printed output doesn't make sense.
There are a few mistakes in your code:
line 4 should not end with a comma.
initialize in class BookCollection is misspelled, resulting in #book_names not being initialized. #book_names therefore equals nil when you attempt to add an element to it with push. nil does not have a method push; hence the exception, and the message printed with the exception.
book_name.push(book_names) should be #book_name.push(book_name). (#book_name must be an instance_variable, as opposed to a local variable, to be visible outside a method, within the class definition.
puts book_collection1 prints the class instance; you want to print #book_names.
Here I've fixed your code. I've used << instead of push. Either is OK, but the former seems to be favored my most.
class Books
attr_accessor :name, :book_id
def initialize(name, book_id)
puts "name = #{name}, book_id = #{book_id}"
#name = name
#book_id = book_id
end
end
class BookCollection
attr :book_names
def initialize
#book_names = []
end
def add_to_books(book_name)
#book_names << book_name
end
end
book_collection1 = BookCollection.new
book1 = Books.new("Catch22", "12345")
book2 = Books.new("Hawaii", "67890")
book_collection1.add_to_books(book1.name)
book_collection1.add_to_books(book2.name)
book_collection1.book_names # => ["Catch22", "Hawaii"]
Probably just a typo at
book_name.push(book_names)
Should have been
book_names.push(book_name)
With attr_accessor :book_names
Let's go to the code directly:
#!/usr/bin/ruby
require 'tk'
class Epg
def initialize
#var = "bad"
#cvs = nil
#items_demo = TkRoot.new() {title "EPG"}
TkFrame.new(#items_demo) {|cf|
#var = "good"
#cvs = TkCanvas.new(cf) {|c|}
puts "#cvs 1 is #{#cvs}"
puts "#var 1 is #{#var}"
}.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
puts "#cvs 2 is #{#cvs}"
puts "#var 2 is #{#var}"
end #initialize
def test
#var = "bad"
puts " #var 3 :#{#var}"
(1..3).each {|x| #var="good"}
puts " #var 4 :#{#var}"
end
end
e= Epg.new
e.test
Here is the output:
#cvs 1 is #<Tk::Canvas:0xb7cecb08>
#var 1 is good
#cvs 2 is
#var 2 is bad ##var has NOT been changed by the code in the block
#var 3 :bad
#var 4 :good ##var has been changed by the code in the block
Why we see different behavior here?
You can think of blocks as closing over both the set of local variables and the current self.
In Ruby, you will always have access to local variables, no matter what. The self encapsulates instance methods on the current object as well as instance variables.
Consider the following code:
class Table
def initialize(legs)
#legs = legs
end
def with_legs
yield #legs
end
end
And then:
def some_calling_method
name = "Yehuda"
Table.new(4) {|legs| puts "#{name} gnaws off one of the #{legs} legs" }
end
By Ruby's block semantics, you can be assured that name will be available inside the block, even without looking at the method you're calling.
However, consider the following:
class Person
def initialize(name)
#name = name
end
def gnaw
Table.new(4).with_legs do |legs|
puts "#{#name} gnaws off one of the #{legs} legs"
end
end
end
Person.new("Yehuda").gnaw
In this case, we are accessing the #name instance variable from inside the block. It works great in this case, but is not guaranteed. What if we implemented table a bit differently:
class Table
def initialize(legs)
#legs = legs
end
def with_legs(&block)
self.instance_eval(&block)
end
end
Effectively, what we're saying is "evaluate the block in the context of a different self." In this case, we are evaluating the block in the context of the table. Why would you do that?
class Leg
attr_accessor :number
def initialize(number)
#number = number
end
end
class Table
def initialize(legs)
#legs = legs
end
def with_leg(&block)
Leg.new(rand(#legs).instance_eval(&block)
end
end
Now, you could do:
class Person
def initialize(name)
#name = name
end
def gnaw
Table.new(4).with_leg do
puts "I'm gnawing off one of leg #{number}"
end
end
end
If you wanted access to the person object inside of the block, you'd have to do:
class Person
def initialize(name)
#name = name
end
def gnaw
my_name = name
Table.new(4).with_leg do
puts "#{my_name} is gnawing off one of leg #{number}"
end
end
end
As you can see, the use of instance_eval can make it simpler and less bulky to access methods of a far-off object inside a block, but comes at the cost of making the self unaccessible. The technique is usually used in DSLs, where a number of methods are injected into the block, but the self doesn't matter that much.
This is what's happening with Tk; they're using instance_eval to inject their own self into the block, which is wiping your self clean.
The explanation is that TkFrame.new uses instance_eval and thus the assignment #var = "good" changes the instance variable of TkFrame. Try this out:
class A
def initialize(&b)
instance_eval(&b)
end
end
class B
def initialize
#x = 10
#a = A.new do
#x = 20
end
end
end
p B.new
This is what you'll see:
#<B:0x10141314 #x=10, #a=#<A:0x10141300 #x=20>>