JD JD wrote in post #1130915:
def reverse
sentence=yield.split
sentence.map do |word|
word.reverse!
end
sentence.join(" ")
end
However, I am still semi lost. I don’t understand the difference
between .map and .each. All I know is using .each doesn’t seem to make
the thing pass. Could someone explain me the difference between the
two?
The difference between #map and #each is what they return. #map is used
to create a new array whose elements are mapped from the original array;
e.g:
a1 = [1, 2, 3]
a2 = a1.map {|x| x * 2 }
p a1 #=> [1, 2, 3]
p a2 #=> [2, 4, 6]
There’s a version called #map! which updates the array; e.g:
a1 = [1, 2, 3]
a1.map! {|x| x * 2 }
p a1 #=> [2, 4, 6]
While #each is used to iterate through the elements without mapping
them, and usually you wouldn’t modify the array from inside the block;
e.g:
a3 = [1, 2, 3]
a3.each {|x| puts x }
Your solution is a valid mixture, even though you used #map instead of
#each; there are two or three alternatives you could do if you want to
make your code look a bit more “correct”:
- use #each ; this one works* because you’re using String#reverse! to
modify each element of the ‘sentence’ array, without changing the array
itself.
def reverse
sentence = yield.split
sentence.each do |word|
word.reverse!
end
sentence.join(" ")
end
- at least, it works for me, in Ruby 1.9.1 and up.
- use the return value from #map ; this works because you’re creating a
second array whose elements are mapped from the first array.
def reverse
sentence = yield.split
backwrds = sentence.map do |word|
word.reverse
end
backwrds.join(" ")
end
- similar to #2, but use #map! so you don’t need a second array
variable.
def reverse
sentence = yield.split
sentence.map! do |word|
word.reverse
end
sentence.join(" ")
end
Also, I am still really confused about this. You are correct, tamouse
m, this was to introduce blocks. However, this set up really confuses
me. How does yield take in the “hello world”? I find it really
confusing. Could someone explain this set up a little more? It appears
it is blocks, but I am used to using blocks in a different context and I
find this set up/thing really confusing. Although, I will be honest,
I’m not super familiar with blocks really. I could probably use a good
refresher (or maybe even a site that explains them if that would be
easier).
I can give you a slightly more complicated example, which might help; it
assumes you have defined any of the #reverse methods listed above:
reverse do
File.open(‘some_words.txt’,‘r’){|f| f.read }
end
When #reverse yields, the block reads the words from a text file. If
you invoked ‘yield’ twice, it would read the file twice. There’s no
limit to the complexity you could add inside that block; the reason it’s
useful is that #reverse doesn’t care how complex the block is, once it
works it will keep on working.
If you’re familiar with javascript’s habit of passing functions as
objects (or the concept of Lambda functions in general) you could even
do this:
get_words = lambda {
words = File.open(‘some_words.txt’,‘r’){|f| f.read }
# blah blah, extra processing
return words
}
obfuscated_words = reverse &get_words
I added a redundant ‘return’ in there, because sometimes people like
that sort of thing. It’s just to demonstrate that ‘get_words’ is a
fully-fledged Lambda function, which you can pass as the block parameter
to #reverse.