0

I am working on the following problem:

describe "some silly block functions" do
  describe "reverser" do
    it "reverses the string returned by the default block" do
      result = reverser do
        "hello"
      end

      expect(result).to eq("olleh")
    end

From my understanding this should reverse a string. My code is as follows:

def reverser
    yield "hello"
end

reverser do |i|
    puts i.reverse
end

This simply returns "hello". I may be missing some fundamental concepts here about how yield, blocks, and functions all interact. How do I going about doing what I am trying to accomplish?

Mladen Jablanović
  • 43,461
  • 10
  • 90
  • 113
John
  • 229
  • 2
  • 10

5 Answers5

4

The answers are good and correct but perhaps it still do not help.

You should start with your spec:

it "reverses the string returned by the default block"

So, it's very clear what your method should do:

def reverser
  # should reverse the string returned by the default block
end

Let's now see how to achieve it. Ok, it should reverse something. But what? Let's see:

string returned by the default block

This suggests that we need to execute the default block and get its returned value. Let's see what the docs say:

yield - Called from inside a method body, yields control to the code block (if any) supplied as part of the method call. ... The value of a call to yield is the value of the executed code block.

So, it seems that your method needs to perform a yield. It will execute a block and return the value the block returns. So, just put a yield there.

def reverser
  yield
end

If you run your spec, it will complain - you will see that the string is still not reversed. So, that's whats left for your method to do:

def reverser
  yield.reverse
end

and that's it.

Mladen Jablanović
  • 43,461
  • 10
  • 90
  • 113
  • So what is a default block? If no block is supplied how is the yield statement actually behaving? Does it take the place of a variable? – John Aug 26 '16 at 16:35
  • I understand the "default block" as a block not assigned to the local variable, not some kind of pre-defined block when no block is supplied when the method is called. So, the one you call with `yield`. – Mladen Jablanović Aug 27 '16 at 19:16
0

You need to include the logic of reversing the string in reverser.

def reverser
  yield.reverse
end

But why bothering using block anyway? It's much clearer to use a normal parameter.

def reverser(str)
  str.reverse
end

reverser('hello')  #=> olleh
Aetherus
  • 8,720
  • 1
  • 22
  • 36
  • The reasons for using a block are numerous, but being able to defer evaluation of an argument is one of them. Changing it to take a regular argument defeats the purpose of this exercise. – tadman Aug 25 '16 at 07:48
0

If you want to put the string to reverse in the block, then you need to get the result of calling the block and reverse it.

def reverser(&block)
  block.call.reverse
end

irb(main):009:0> result = reverser do
irb(main):010:1*   "hello"
irb(main):011:1> end
=> "olleh"
kcdragon
  • 1,724
  • 1
  • 14
  • 23
  • Declaring the block here is extraneous, overly verbose and confusing. `yield` exists for a reason. – tadman Aug 25 '16 at 07:46
  • I was trying to give OP an answer he would understand. For people new to block, the explicit nature can be easier to understand. – kcdragon Aug 25 '16 at 12:01
  • It would be better if you explain such things before throwing a bizarre and unfamiliar notation at people like how `&` captures blocks. – tadman Aug 25 '16 at 18:20
0

I know it's been a year but this hasn't been answered right.

def reverser
  out = []
  yield.split.each{|word| out << word.reverse}
  out.join(" ")
end

I'm pretty sure it has to do with scope

-1

I agree with the above responses - they make the most sense. but want to add why your code isn't working and how to fix it:

expect(result).to eq("olleh")

So according to that you want result to return a string. Is it doing that?

  1. puts returns nil. when you have puts at the end of a method - be aware that the method will return nil. It's insidious because sometimes the results are not what is expected.
  2. but you are expecting it to return 'olleh'
  3. get rid of the puts and it should work like you expect (untested)
def reverser
  yield "hello"
end

reverser do |i|
  i.reverse  # NOTE THAT THE PUTS is missing here
end

I think that's what you are looking for.

edit: Please test and let me know because some folks think I have the answer completely wrong! of course you'd not want to rely on the particular block that you are using as a design point, but this should give you an idea of why it wasn't working

BenKoshy
  • 33,477
  • 14
  • 111
  • 80
  • Exercising the function once in a particular way doesn't change its behaviour permanently. This is an incorrect answer, you fell into the same trap as the person asking the question. – tadman Aug 25 '16 at 07:47
  • @tadman - thanks - can you please elaborate? when i tested it in ruby (not rspec) it returned what i expected. eliminating the puts eliminated the problem because it was otherwise causing the error. – BenKoshy Aug 25 '16 at 10:28
  • You've defined a `reverser` function that returns `"hello"` and then you exercise it once which, because of the block you've sent it, does the operation. This produces the correct output but gets the priorities backwards: The `reverser` method is supposed to reverse the string, the block is supposed to supply the string to be reversed. – tadman Aug 25 '16 at 18:24