Learn Ruby the Hard Way #41 [closed] - ruby

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Hi I`m learning from LRtHW and I got stuck....
I have program like this:
require 'open-uri'
WORD_URL = "http://learncodethehardway.org/words.txt"
WORDS = []
PHRASES = {
"class ### < ###\nend" => "Make a class named ### that is-a ###.",
"class ###\n\tdef initialize(###)\n\tend\nend" => "class ### has-a initialize that takes ### parameters.",
"class ###\n\tdef ***(###)\n\tend\nend" =>"class ### has-a function named *** that takes ### parameters.",
"*** = ###.new()" => "Set *** to an instance of class ###.",
"***.***(###)" => "From *** get the *** function, and call it with parameters ###.",
"***.*** = '***'" => "From *** get the *** attribute and set it to '***'."
}
PHRASE_FIRST = ARGV[0] == "english"
open(WORD_URL) do |f|
f.each_line {|word| WORDS.push(word.chomp)}
end
def craft_names(rand_words, snippet, pattern, caps=false)
names = snippet.scan(pattern).map do
word = rand_words.pop()
caps ? word.capitalize : word
end
return names * 2
end
def craft_params(rand_words,snippet,pattern)
names = (0...snippet.scan(pattern).length).map do
param_count = rand(3) + 1
params = (0...param_count).map {|x| rand_words.pop()}
params.join(', ')
end
return names * 2
end
def convert(snippet, phrase)
rand_words = WORDS.sort_by {rand}
class_names = craft_names(rand_words, snippet, /###/, caps=true)
other_names = craft_names(rand_words, snippet,/\*\*\*/)
param_names = craft_params(rand_words, snippet, /###/)
results = []
for sentence in [snippet, phrase]
#fake class name, also copies sentence
result = sentence.gsub(/###/) {|x| class_names.pop}
#fake other names
result.gsub!(/\*\*\*/) {|x| other_names.pop}
#fake parameter list
result.gsub!(/###/) {|x| param_names.pop}
results.push(result)
end
return results
end
# keep going until they hit CTRL-D
loop do
snippets = PHRASES.keys().sort_by { rand }
for snippet in snippets
phrase = PHRASES[snippet]
question, answer = convert(snippet, phrase)
if PHRASE_FIRST
question, answer = answer, question
end
print question, "\n\n> "
odp = gets.chomp
if odp == "exit"
exit(0)
end
#exit(0) unless STDIN.gets
puts "\nANSWER: %s\n\n" % answer
end
end
I understand most of this code, but I have a problem with:
for sentence in [snippet, phrase]
I know that it is a "for" loop and it creates a "sentence" variable, but how does the loop know that it need to look in a key and value of hash "PHRASES"
And my second "wall" is:
question, answer = convert(snippet, phrase)
It looks like it creates and assigns "question" and "answer variables to the "convert" method with "snippet" and "phrase" parameters... again how does it assigns "question" to a key and answer to a value.
I know that this is probably very simple but as for now it blocks my mind :(

For your first question about the for-loop:
Look at where the for-loop is defined. It's inside the convert() method, right? And the convert() method is passed two arguments: one snippet and one phrase. So the loop isn't "looking" for values in the PHRASES hash, you are the one supplying it. You're using the method's arguments.
For your second question about assignment:
In Ruby we can do something called "destructuring assignment". What this means is that we can assign an array to multiple variables, and each variable will hold one value in the array. That's what's happening in your program. The convert() method returns a two-item array, and you're giving a name (question and answer) to each item in the array.
Here's another example of a destructuring assignment:
a, b, c = [1, 2, 3]
a # => returns 1
b # => returns 2
c # returns 3
Try this out in IRB and see if you get the hang of it. Let me know if I can help clarify anything, or if I misunderstood your question. You should never feel bad about asking "simple" questions!

Related

How to get hash in a given array list in Ruby? [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
My Ruby question is in a given array of non-empty strings, create and return a hash as follows: for each string add its first character as a key with its last character as a value in defn method
def pairs1(arry)
arry = [ "code", "bug"]
final_hash = {}
pairs1.each do |var|
final_hash[] = puts var[0] puts var[-1]
final_hash[var[0]] = var[-1]
end
puts final_hash
The error I got is :
syntax error, unexpected tIDENTIFIER, expecting keyword_end
...final_hash[] = puts var[0] puts var[-1]
... ^~
Looking at your code:
def pairs1(arry)
arry = [ "code", "bug"]
final_hash = {}
pairs1.each do |var|
final_hash[] = puts var[0] puts var[-1]
final_hash[var[0]] = var[-1]
end
puts final_hash
A few things are a bit off. You should remove the array initialization (arry = [...]) because you're passing in a array through the parameters of the method already. If you keep it like this, passing a parameter won't have any effect on the method. Also this line final_hash[] = puts var[0] puts var[-1] doesn't do anything (aside from raising an error), so you can remove it too. You also want to call the #each methon on the array, calling it on the method itself doesn't make much sense here. If you also add an end to the end of the method you get a working version of your method:
def pairs1(arry)
final_hash = {}
arry.each do |var|
final_hash[var[0]] = var[-1]
end
puts final_hash
end
Keep in mind, this method currently doesn't return the value of final_hash, it just prints the value to the console and returns nil. If you want to change that just remove the puts from the last line.
You can also do something like this:
arry = [ "code", "bug"]
def pairs1(arry)
arry.each_with_object({}) { |element,hash| hash[element[0]] = element[-1] }
end
pairs1(arry)
# => {"c"=>"e", "b"=>"g"}
You forgot to add end to the close defining of the method.
You can use inject method too. And for readability I prefer chars.first and chars.last
arry = ['code', 'bug']
final_hash = arry.inject({}) do |memo, val|
memo[val.chars.first] = val[val.chars.last]
memo
end
p final_hash
{"c"=>"e", "b"=>"g"}

How do i print a string with repeated names followed by a number - Ruby? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
How do i print a name Ex. John1 and then print after John2, john3, john4, john5 and so one in an infinite number
I started using ruby today and I'm having some trouble figuring this out. I've searched some but can't find anything so how would I do this?
It's to mass produce accounts to the staff of a certain company and I found ruby to be the smartest to write it in and I will never use it again after this
Keep it simple.
puts (1..5).map { |n| "john#{n}" }
john1
john2
john3
john4
john5
The class Range includes the module Enumerable (as does the classes Array, Hash and others). By doing so, instances of Range (such as 1..5) gain the use of all of Enumerable's instance methods. One of Enumerable's instance methods is used here: Enumerable#map.
For printing a simple series like this:
n = 1
loop do
print 'john%d, ' % n
n += 1
end
That will never terminate, which makes it kind of silly. Maybe what you want is a bounded range:
list = (1..10).map do |n|
'john%d' % n
end.join(', ')
puts list
You can adjust the start and end values as necessary.
Perhaps use an enumerator here:
enum = Enumerator.new do |y|
i = 1
loop do
y << "John#{i}"
i += 1
end
end
enum.next #=> "John1"
enum.next #=> "John2"
enum.next #=> "John3"
Then use any one of the methods available to instances of Enumerator. Here we've used Enumerator#next to get the next "John" string.
One simple way is using a for loop. First declare an empty string variable that will hold our contents.
One important thing to consider is the index of the loop. If it's the last item, we do not want to add a separator like ", "
This is where the conditional comes into play. If the index is less than the last, we will add a comma and space, otherwise just the name.
Interpolation is done by wrapping a variable inside #{ and }
str = ""
for i in 1..5
str += i < 5 ? "john#{i}, " : "john#{i}"
end
Returns
"john1, john2, john3, john4, john5"

How to solve ruby string [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I'm new to ruby, and I'm trying to make a simple calculator in which a user types in a simple problem (such as addition or subtraction) and the answer is returned. The problem is when the user types in a question, the question itself is being returned instead of the answer to that question.
puts "How many Questions?"
questions = gets.chomp.to_i
questions.times do |problem|
puts "question: "
problem = gets.chomp
puts "answer: #{problem}"
end
Inside your loop, instead of:
problem = gets.chomp
puts "answer: #{problem}"
Try this:
problem = gets.chomp
solved_problem = eval(problem)
puts "answer : #{solved_problem}"
eval will take care of interpreting your string as a Ruby instruction. But it's very messy, because anyone could write any Ruby program in your input and eval will make it run, so it's not safe at all.
If you only want to take care of simple operations, you can use a regex first to check if the input string looks like what you want:
problem_is_ok = (problem =~ /^\d+ *[+-] *\d+$/)
This will be true if the string starts with a number, followed by 0 to many spaces, followed by either a + or - sign, followed by 0 or more spaces, followed by another number and nothing else. Then you raise an error if this is not true.
Your loop now look like this:
questions.times do |problem|
puts "question: "
problem = gets.chomp
problem_is_ok = (problem =~ /^\d+ *[+-] *\d+$/)
if problem_is_ok
puts "answer: #{eval(problem)}"
else
#I raise an error, but you might aswell just print it instead for your simple program
raise ArgumentError("Can't compute this")
end
end
Add and subtract can be simple ruby definitions
We pass in 5 and 1
The lower portion of the code is the clear user interface implementation
when we loop we do it 3 times
It outputs 3 options for the user to select from
We read in with chomp, standard in, the keyboard, chooses 1, 2, or 3
If elsif statements conditionally select for each case
Using string interpolation we render the variables a and b into a new string,
and run their respective methods (add or subtract)
Converting that methods integer output to a string, and concatenate it.
Outputting that to the users screen
The 3rd option does no calculation,
instead it prints to users screen a simple string
our else condition catches the case when people don't enter one of the choices of 1, 2 or 3
It tells you to correct your choice to the options provided
Hope this helps
#!/usr/bin/env ruby
questions = 3
a, b = 5, 1
def add(a,b)
a + b
end
def subtract(a,b)
a - b
end
questions.times do |questions|
puts "Please choose:
1. add
2. subtract
3. exit"
questions = gets.chomp
if questions == '1'
puts "#{a} + #{b} = " + add(a,b).to_s
elsif questions == '2'
puts "#{a} - #{b} = " + subtract(a,b).to_s
elsif questions == '3'
puts 'exiting, goodbye.'
exit
else
p 'please choose again'
end
end

Separate subhash from hash [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have the following hash_of_student_and_grades.
{132=>{"Design"=>6, "English"=>5, "Humanities"=>5, "Languages"=>6, "Math"=>6, "Music"=>7,
"PE"=>6, "Science"=>6, "Theatre"=>6},
134=>{"Design"=>7, "English"=>6, "Humanities"=>6, "Languages"=>6, "Math"=>5, "Music"=>6,
"PE"=>6, "Science"=>7, "Art"=>6},
136=>{"Design"=>5, "English"=>4, "Humanities"=>5, "Languages"=>6, "Math"=>6, "Music"=>6,
"PE"=>7, "Science"=>5, "Theatre"=>6},...}
Now I want to make hash with key like this.
id132={"Design"=>6, "English"=>5, "Humanities"=>5, "Languages"=>6, "Math"=>6, "Music"=>7,
"PE"=>6, "Science"=>6, "Theatre"=>6}
id134={"Design"=>7, "English"=>6, "Humanities"=>6, "Languages"=>6, "Math"=>5, "Music"=>6,
"PE"=>6, "Science"=>7, "Art"=>6}
...
...
UPDATE:
I have done this so far. But it does not assign each hash to key.
resulthash.each {|key, value| puts key=value}
# outputs
{"Design"=>6, "English"=>5, "Humanities"=>5, "Languages"=>6, "Math"=>6, "Music"=>7, "PE"=>6, "Science"=>6, "Theatre"=>6}
{"Design"=>7, "English"=>6, "Humanities"=>6, "Languages"=>6, "Math"=>5, "Music"=>6, "PE"=>6, "Science"=>7, "Theatre"=>6}
How can I accomplish this?
You can't use digits as variable names, but if it would be id132, id133 etc you can accomplish this using metaprogramming and instance variables:
hash = {"i1"=>{"a"=>"b"}, "i2"=>{"c"=>"d"}}
hash.each { |key, value| instance_variable_set("##{ key }", value) }
puts #i1 # => {"a"=>"b"}
Because you can(ruby gives you ability to shoot your foot),here is answer:
In ruby id32 is the name of variable.
Hash's key can be any type: number, string... object.
Basic types(number, string, nil, false, true) are evaluated like this:
n1 = 2
n2 = 2
h = {2 => 42}
h[n1] # 42
h[n2] # 42
In other words you get/set hash's key 2 not n1 or n2. It would be hard to change basic types.
Objects, on other hands, you can modify:
class N
def initialize name
#name=name #contains name, like `id32`, `id34`
end
def to_s #this method let you show any text, here name(for example `id32`)
"#{#name}"
end
end
id32 = N.new 'id32'
h={id32 => 42}
#{id32=>42}
#adding more keys/values
id34 = N.new 'id34'
h[id34] = 'some value'
and... that's just basics.
what lots of rubist do:
They use symbols.
What are symbols?
It's type like string but there can be only one symbol!
puts :a.object_id
puts :a.object_id
puts :a.object_id
Code above prints 3 the same numbers because :a is only 1.
puts 'a'.object_id
puts 'a'.object_id
Strings are not the same.
Construction:
You prepend characters with ::
`:a`
or if it has weird character, prepend with : and surround with ' or ":
:'a'
To sum up:
Your code will look exactly the same, will be faster(no need for objects like above):
h ={:a => :b}
or from 1.9.x(i don't remember correct number) you can use this syntax too:
h = {a: :b}

Converting string into class [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
What I do need:
I pass a string that has to set an unmutable object inside an array, but I do not know hot how to make the transition from the string that the user inputs to the object name I need.
What I am intending to do:
I am working on a conversational adventure. The key point is to have a function that creates a command prompt so the user can interact with the game. Whenever the user says "go to somewhere", there is another function called "goto" that compares whether the input is included in the exits of the place where the player is; if so, the attribute "place" for the player takes a new place.
What I did:
I made a command prompt that actually works*
loop do
print "\n >>> "
input = gets.chomp
sentence = input.split
case
when sentence[0] == "inspect"
$action.inspect(sentence[1])
when sentence[0] == "go" && sentence[1] == "to"
$action.goto(sentence[2])
when sentence[0] == "quit"
break
else
puts "\nNo le entiendo Senor..."
end
And I initialized the objects as I need them (the third attribute goes for the exits):
room = Place.new("room", "Room", [newroom], "This is a blank room. You can _inspect_ the -clock- or go to [newroom].", ["this this"])
newroom = Place.new("newroom", "New Room", [room], "This is another blank room. You can _inspect_ the -clock-", ["this this"])
Then I made a method inside the action controller that has to compare and set the places properly. (Beware: monster newbie code following. Protect you eyes).
def goto(destiny) #trying to translate the commands into variable names
if (send "#{destiny}").is_in? $player.place.exits
$player.place = send "#{sentence[2]}"
puts destiny.description
else
puts "I cannot go there."
end
end
I think you want to convert a string to constant. Well it is easy. Read an example:
string = 'Hash'
const = Object.const_get(string) #=> Hash
const.new #=> {}; <- it is an empty Hash!
But be careful. If there's no such a constant you will get uninitialized constant error. In this case your adventures will stop.
I hope I understood your question and you will understand my answer.
How to change string to object, there are few options:
Bad(eval family):
eval("name_of_your_variable = #{21+21}")
eval("puts name_of_your_variable") #42
You can see that eval can make everything. So use with caution.
However, as pointed by #user2422869 you need(be in) scope - place where your variables are saved. So above code won't run everywhere
Everytime you run following method you create another scope
def meth1
puts "defined: #{(defined? local_a) ? 'yes' : 'no'}!"
eval 'local_a = 42'
local_a += 100
eval 'puts local_a'
end
meth1
and here is output:
defined: no!
142
If you want to grab local_a from one of scopes of meth1 you need binding.
def meth2
var_a = 222
binding
end
bin = meth2
bin.eval 'var_a'
#output:
#222
About binding you can read in doc. As for scopes, I don't have good site.
Better:
hash_variable = Hash.new # or just {}
hash[your_string_goes_here] = "some value #{42}"
puts hash[your_string_goes_here]
I don't know if good or bad:
As for this: send "#{destiny}". I assume that your destiny doesn't exist, so you can use method_missing:
def method_missing arg, *args, &block
#do some with "destiny"; save into variable/hash, check if "destiny" is in right place etc.
# return something
end

Resources