The first thing I noticed was that its behavior is different from the C# yield statement (which I knew from before).

Ruby's yield statement gives control to a user specified block from the method's body. A classic example is the Fibonacci Sequence:

class NumericSequences

def fibo(limit)

i = 1

yield 1

yield 1

a = 1

b = 1

while (i < limit)

t = a

a = a + b

b = t

yield a

i = i+1

end

end

...

end

The

`fibo`

method can be used by specifying a block that will be executed each time the control reaches a `yield`

statement. For example:

irb(main):001:0> g = NumericSequences::new

=> #<NumericSequences:0xb7cd703c>

irb(main):002:0> g.fibo(10) {|x| print "Fibonacci number: #{x}\n"}

Fibonacci number: 1

Fibonacci number: 1

Fibonacci number: 2

Fibonacci number: 3

Fibonacci number: 5

Fibonacci number: 8

Fibonacci number: 13

Fibonacci number: 21

Fibonacci number: 34

Fibonacci number: 55

Fibonacci number: 89

The following example shows the sequence of numbers of a specific row of the Pascal's triangle :

def pascal_triangle_row(n)

for k in 0..n

yield(fact(n)/(fact(k)*fact(n-k)))

end

end

This method can be used like this:

irb(main):001:0> g = NumericSequences::new

=> #<NumericSequences:0xb7c9103c>

irb(main):002:0> (0..8).each {|n| g.pascal_triangle_row(n) {|r| print "#{r} "}; print "\n"}

1

1 1

1 2 1

1 3 3 1

1 4 6 4 1

1 5 10 10 5 1

1 6 15 20 15 6 1

1 7 21 35 35 21 7 1

1 8 28 56 70 56 28 8 1

=> 0..8

Methods using the

`yield`

statement can be combined. For example in order to create the sequence of Pascal's triangle read by rows we can write:

def pascal(limit_row)

i = 0

while (i <= limit_row)

pascal_triangle_row(i){|x| yield(x)}

i = i+1

end

end

And use it the same way:

irb(main):005:0> g.pascal(10) {|x| print "#{x} "}

1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 1 10 45 120 210 252 210 120 45 10 1 => nil

## 6 comments:

Could you explain how this is different than C#'s yeild? It looks to be doing the same thing from the examples you gave. Am I missing something?

Sorry I didn't explain why I think c#'s yield is different.

I think there's no big difference, but for me, the way you use the result of a method call is different.

In C# when you use yield the type of the method must be IEnumerable<T> where T is the type of the element you are returning. This means you can pass the result of a calling that method to another method or assign it to a variable. For example:

class P {

IEnumerable<int> Numbers()

{

yield return 1;

yield return 2;

yield return 3;

}

void UseNumbers()

{

IEnumerable<int> v = Numbers();

Foo(Numbers());

}

void Foo(IEnumerable<int> g)

{

foreach(int i in g ) {

Console.WriteLine(i);

}

}

}

From my limited Ruby knowledge, I think the difference is that in order to use a method that has yield you need to specify a block as an argument of the method call (like in the example I use in this post).

If you want to pass the result you can use Enumerator::Enumerable class.

r = P::new

def foo(e)

e.each { |i| print i}

end

en = Enumerator::Enumerable::new(r,:numbers)

foo(en)

One think I didn't know how to implement using Ruby's yield is how to create an infinite sequence of values , combine it with other sequences and take only some elements of the sequence (which was the original example I was trying to implement for this post). For example in C# you can write:

IEnumerable<int> Odd()

{

int i = 1;

while(true)

{

if (i % 2 != 0) {

yield return i;

}

i++;

}

}

IEnumerable<int> Even()

{

int i = 1;

while(true)

{

if (i % 2 == 0) {

yield return i;

}

i++;

}

}

IEnumerable<T> Mix<T>(IEnumerable<T> e1,IEnumerable<T> e2)

{

IEnumerator<T> i1 = e1.GetEnumerator();

IEnumerator<T> i2 = e2.GetEnumerator();

bool c1 = true;

bool c2 = true;

while (c1 || c2)

{

if (i1.MoveNext())

{

yield return i1.Current;

} else {

c1 = false;

}

if (i2.MoveNext())

{

yield return i2.Current;

} else {

c2 = false;

}

}

}

IEnumerable<T> Take<T>(int n,IEnumerable<T> e) {

int i =1;

foreach(T r in e)

{

if (i > n)

break;

yield return r;

i++;

}

}

You can write the following code:

foreach(int i in Take(6,Mix(Odd(),Even())) ) {

Console.WriteLine(i);

}

And the program will print only the numbers from 1 to 6.

I'm going to read more to see if this could be implemented.

Here's a neater version of fibo:

def fibs(x)

a = b = 1

x.times{yield a; a,b = b,a+b}

end

to be used like this:

fibs(10){|fib| puts fib}

Very nice!

As far as I know yield in Ruby is just syntax sugar for calling implicit last lambda parameter. So it's less powerful then Python's or C#'s analogs. E.g. in python one can write following, but not in Ruby:

def indices(n):

. i = 0

. while n>i:

.. yield i

.. i += 1

def fib():

. a = b = 1

. while True:

.. yield a

.. a,b = b,a+b

list(zip(index(5), fib()))

Post a Comment