Hi I'm trying to solve the exercise from https://github.com/alexch/learn_ruby
I must write a method which should "multiplies two numbers" and "multiplies an array of numbers". I'm fresh to ruby and solved it, with only one method like this:
def multi(*l)
sum = 1
l.flatten! if l.is_a? Array
l.each{|i| sum = sum*i}
return sum
end
I'm sure there are better ways, so how can I improve this method? to a more ruby like syntax :)
The if l.is_a? Array is not necessary because the way multi is define, l will always be an array.
The pattern
result = starting_value
xs.each {|x| result = result op x}
result
can be written more succinctly using xs.inject(starting_value, :op).
So you can write your code as:
def multi(*l)
l.flatten.inject(1, :*)
end
If you're ok, with calling the method as multi(*array) instead of multi(array) to multiply an array, you can leave out the flatten, as well.
Related
I'm working on a mini project for a summer class. I'd like some feedback on the code I have written, especially part 3.
Here's the question:
Create an array called numbers containing the integers 1 - 10 and assign it to a variable.
Create an empty array called even_numbers.
Create a method that iterates over the array. Place all even numbers in the array even_numbers.
Print the array even_numbers.
Here's my code, so far:
numbers = [1,2,3,4,5,6,7,8,9,10]
print numbers[3]
even_numbers.empty?
def even_numbers
numbers.sort!
end
Rather than doing explicit iteration, the best way is likely Array#select thus:
even_numbers = numbers.select { |n| n.even? }
which will run the block given on each element in the array numbers and produce an array containing all elements for which the block returned true.
or an alternative solution following the convention of your problem:
def get_even_numbers(array)
even_num = []
array.each do |n|
even_num << n if n.even?
end
even_num
end
and of course going for the select method is always preferred.
I'm trying to reduce the while loop below to a single line
def this_method(week)
i = 0
while i < array.length
yield(week[i])
i += 1
end
end
week.each do |week|
puts week
end
Like others, I'm confused about the example (array is not defined, and this_method is never called). But you certainly don't need the while loop. I'd just use the Integer#times method, since you're making no use of the array values:
array.length.times {|i| yield week[i]}
#each_index (which ram suggested) works just as well.
But if array is actually meant to be week, then it gets even simpler:
week.each {|x| yield x}
I'm not sure why you'd want to create a method that just recycles #each though.
For since line you can use Array#each_index:
array.each_index {|i| yield week[i] }
No, you can't. The ternary operator is a conditional expression, the while is a loop expression.
However, in Ruby you normally use enumerators, not while. Your code can be rewritten as
def this_method(week)
array.each_with_index { |item, i| yield(week[i]) }
end
What is not clear to me, is there the array variable comes from. Even in your example, there is no definition of such variable.
if in any form check conditions only once.
while on other hand, can check conditions many times.
Well, if you don't like other answers with enumerators you can use while in a different form:
def this_method(week)
i = -1
yield(week[i]) while (i+=1) < array.length
end
I'm doing a SaaS course with Ruby. On an exercise, I'm asked to calculate the cartesian product of two sequences by using iterators, blocks and yield.
I ended up with this, by pure guess-and-error, and it seems to work. But I'm not sure about how. I seem to understand the basic blocks and yield usage, but this? Not at all.
class CartProd
include Enumerable
def initialize(a,b)
#a = a
#b = b
end
def each
#a.each{|ae|
#b.each{|be|
yield [ae,be]
}
}
end
end
Some explanation for a noob like me, please?
(PS: I changed the required class name to CartProd so people doing the course can't find the response by googling it so easily)
Let's build this up step-by-step. We will simplify things a bit by taking it out of the class context.
For this example it is intuitive to think of an iterator as being a more-powerful replacement for a traditional for-loop.
So first here's a for-loop version:
seq1 = (0..2)
seq2 = (0..2)
for x in seq1
for y in seq2
p [x,y] # shorthand for puts [x, y].inspect
end
end
Now let's replace that with more Ruby-idiomatic iterator style, explicitly supplying blocks to be executed (i.e., the do...end blocks):
seq1.each do |x|
seq2.each do |y|
p [x,y]
end
end
So far, so good, you've printed out your cartesian product. Now your assignment asks you to use yield as well. The point of yield is to "yield execution", i.e., pass control to another block of code temporarily (optionally passing one or more arguments).
So, although it's not really necessary for this toy example, instead of directly printing the value like above, you can yield the value, and let the caller supply a block that accepts that value and prints it instead.
That could look like this:
def prod(seq1, seq2)
seq1.each do |x|
seq2.each do |y|
yield [x,y]
end
end
end
Callable like this:
prod (1..2), (1..2) do |prod| p prod end
The yield supplies the product for each run of the inner loop, and the yielded value is printed by the block supplied by the caller.
What exactly do you not understand here? You've made an iterator that yields all possible pairs of elements. If you pass CartProd#each a block, it will be executed a.length*b.length times. It's like having two different for cycles folded one into another in any other programming language.
yield simply passes (yields) control to a block of code that has been passed in as part of the method call. The values after the yield keyword are passed into the block as arguments. Once the block has finished execution it passes back control.
So, in your example you could call #each like this:
CartProd.new([1, 2], [3, 4]).each do |pair|
# control is yielded to this block
p pair
# control is returned at end of block
end
This would output each pair of values.
Consider a simple Enumerator like this:
natural_numbers = Enumerator.new do |yielder|
number = 1
loop do
yielder.yield number
number += 1
end
end
My question is: Why does ruby require that we invoke yield on the yielder object? Said another way: Why can't we replace yielder.yield number with yield number? In this example, it would be appear to be the same thing, if it were allowed. Are there examples where yielder is used in a nontrivial way? If so, can you give one? If not, what is the purpose of yielder?
Thanks.
Not 100% sure if that's the reason, but yield alone (always) applies to the block submitted to the method which calls yield: in your case the method which contains natural_numbers assignment; and it's not possible for it to perform what is desired for Enumerator, i.e. to emit the Enumerator element. Although bearing the same name, Yielder#yield is a method, and Ruby's yield is a statement.
In other words, it would not be possible to implement Enumerator constructor which would work with yield statement.
I have a method that returns an array of arrays.
For convenience I use collect on a collection to gather them together.
arr = collection.collect {|item| item.get_array_of_arrays}
Now I would like to have a single array that contains all the arrays.
Of course I can loop over the array and use the + operator to do that.
newarr = []
arr.each {|item| newarr += item}
But this is kind of ugly, is there a better way?
There is a method for flattening an array in Ruby: Array#flatten:
newarr = arr.flatten(1)
From your description it actually looks like you don't care about arr anymore, so there is no need to keep the old value of arr around, we can just modify it:
arr.flatten!(1)
(There is a rule in Ruby that says that if you have two methods that do basically the same thing, but one does it in a somewhat surprising way, you name that method the same as the other method but with an exlamation point at the end. In this case, both methods flatten an array, but the version with the exclamation point does it by destroying the original array.)
However, while in this particular case there actually is a method which does exactly what you want, there is a more general principle at work in your code: you have a sequence of things and you iterate over it and try to "reduce" it down into a single thing. In this case, it is hard to see, because you start out with an array and you end up with an array. But by changing just a couple of small details in your code, it all of the sudden becomes blindingly obvious:
sum = 0
arr.each {|item| sum += item } # assume arr is an array of numbers
This is exactly the same pattern.
What you are trying to do is known as a catamorphism in category theory, a fold in mathematics, a reduce in functional programming, inject:into: in Smalltalk and is implemented by Enumerable#inject and its alias Enumerable#reduce (or in this case actually Array#inject and Array#reduce) in Ruby.
It is very easy to spot: whenever you initialize an accumulator variable outside of a loop and then assign to it or modify the object it references during every iteration of the loop, then you have a case for reduce.
In this particular case, your accumulator is newarr and the operation is adding an array to it.
So, your loop could be more idiomatically rewritten like this:
newarr = arr.reduce(:+)
An experienced Rubyist would of course see this right away. However, even a newbie would eventually get there, by following some simple refactoring steps, probably similar to this:
First, you realize that it actually is a fold:
newarr = arr.reduce([]) {|acc, el| acc += el }
Next, you realize that assigning to acc is completely unnecessary, because reduce overwrites the contents of acc anyway with the result value of each iteration:
newarr = arr.reduce([]) {|acc, el| acc + el }
Thirdly, there is no need to inject an empty array as the starting value for the first iteration, since all the elements of arr are already arrays anyway:
newarr = arr.reduce {|acc, el| acc + el }
This can, of course, be further simplified by using Symbol#to_proc:
newarr = arr.reduce(&:+)
And actually, we don't need Symbol#to_proc here, because reduce and inject already accept a symbol parameter for the operation:
newarr = arr.reduce(:+)
This really is a general pattern. If you remember the sum example above, it would look like this:
sum = arr.reduce(:+)
There is no change in the code, except for the variable name.
arr.inject([]) { |main, item| main += item }
I don't seem to understand the question fully... Is Array#flatten what you are looking for?
[[:a,:b], [1,2,3], 'foobar'].flatten
# => [:a, :b, 1, 2, 3, 'foobar']