Is there a method in Ruby that does the opposite of find? - ruby

a, b, c = 0, 1, 2
[a, b, c].find(&:zero?) # => 0
Is there any method that finds the first element for which the block returns false?
[a, b, c].the_method(&:zero?) # => 1
In other words, it would behave the same way as:
[a, b, c].reject(&:zero?).first

There is not, but you could create one either the clean-ish way:
a = [0,2,1,0,3]
module Enumerable
def first_not(&block)
find{ |x| !block[x] }
end
end
p a.first_not(&:zero?)
#=> 2
...or the horribly-amusing hack way:
class Proc
def !
proc{ |o,*a| !self[o,*a] }
end
end
p a.find(&!(:zero?.to_proc))
#=> 2
...or the terse-but-terribly-dangerous way:
class Symbol
def !
proc{ |o,*a| !o.send(self,*a) }
end
end
p a.find(&!:zero?)
#=> 2
But I'd advocate just skipping the tricky Symbol#to_proc usage and saying what you want:
p a.find{ |i| !i.zero? }
#=> 2

If you're using Ruby 2.0, you may be able to do lazy.reject(&:zero?).first without the performance penalty of going through the full array.

As far as I can tell there is not a standard method to do this (given that find_all and reject reference each other, but find does not reference anything). If you need it frequently (especially if the reject is too slow) you can write your own
module Enumerable
def first_reject(&block)
find {|a| not (block.call(a)) }
end
end

Related

How can I modify my ruby method so it takes in a block of code as well?

I have a method called myFilter that takes in an array, and filters out the elements that don't meet the requirement.
For example.
arr = [4,5,8,9,1,3,6]
answer = myfilter(arr) {|i| i>=5}
this run would return an array with elements 5,8,9,6 since they are all greater than or equal to 5.
How would I preform this? the algorithm is easy, but I don't understand how we take in that condition.
Thank you.
I take for granted you don't want to use select method or similar but you want to understand how blocks work.
def my_filter(arr)
if block_given?
result = []
arr.each { |element| result.push(element) if yield element } # here you use the block passed to this method and execute it with the current element using yield
result
else
arr
end
end
The idiomatic way would be:
def my_filter(arr)
return enum_for(:my_filter, arr) unless block_given?
arr.each_with_object([]) do |e, acc|
acc << e if yield e
end
end
More info on Enumerator::Lazy#enum_for.
you can do
def my_filter(arr, &block)
arr.select(&block)
end
then call
my_filter([1, 2, 3]) { |e| e > 2 }
=> [3]
but instead you can just call select with a block directly :)

Trying to write my own all? method in Ruby

There is a method called all? in Enumerable.
I'm trying to learn all the methods of Enumberable's library by writing them myself.
This is what I've come up so far for the all? method. I sorta understand it but I got stumped when trying to pass initialized values to my method.
EDIT for the record, I'm aware that enum method that I have is not the right way ie, it's hard-coded array. This is for self-learning purposes. I'm just trying to figure out how to pass the initialized values to my all? method. That's why I wrote enum in the first place, to see that it is working for sure. Please don't take this class as a literal gospel. Thank you.
class LearningMethods
def initialize(values)
#values = values
end
def enum
array = [10, 3, 5]
end
def all?(a)
yield(a)
end
end
c = LearningMethods.new([10, 3, 5])
p c.enum.all? {|x| x >= 3 } #this works
p c.all?(10) { |x| x >= 3 } #this works
p c.all?(#values) { |x| x >= 3 } #this doesn't work. Why not? And how do I pass the initialized values?
I'm not sure why you need enum at all? Enumerable is a module included in array, so if you're not familiar with this I recommend you read about "modules and mix-ins" in Ruby.
all? works simply by passing EACH of the array elements to the block. If there is ANY element (at least 1) for which the block returns false, then all? evaluates to false. Try analyzing this code:
class MyAllImplementation
def initialize(array)
#array = array
end
def my_all?
#array.each do |element| # for each element of the array
return true unless block_given? # this makes sure our program doesn't crash if we don't give my_all? a block.
true_false = yield(element) # pass that element to the block
return false unless true_false # if for ANY element the block evaluates to false, return false
end
return true # Hooray! The loop which went over each element of our array ended, and none evaluted to false, that means all elements must have been true for the block.
end
end
a = MyAllImplementation.new([1,2,3])
p a.my_all? { |x| x > 0 } #=> true
p a.my_all? { |x| x > 1 } # false, because 1 is not bigger than 1, it's equal to 1

How can I create an enumerator that does certain things after iteration?

How can I create an enumerator that optionally takes a block? I want some method foo to be called with some arguments and an optional block. If it is called with a block, iteration should happen on the block, and something should be done on it, involving the arguments given. For example, if foo were to take a single array argument, apply map on it with the given block, and return the result of join applied to it, then it would look like:
foo([1, 2, 3]){|e| e * 3}
# => "369"
If it is called without a block, it should return an enumerator, on which instance methods of Enumerator (such as with_index) should be able to apply, and execute the block in the corresponding way:
enum = foo([1, 2, 3])
# => Enumerator
enum.with_index{|e, i| e * i}
# => "026"
I defined foo using a condition to see if a block is given. It is easy to implement the case where the block is given, but the part returning the enumerator is more difficult. I guess I need to implement a sublass MyEnum of Enumerator and make foo return an instance of it:
def foo a, &pr
if pr
a.map(&pr).join
else
MyEnum.new(a)
end
end
class MyEnum < Enumerator
def initialize a
#a = a
...
end
...
end
But calling MyEnum.new raises a warning message: Enumerator.new without a block is deprecated; use Object#to_enum. If I use to_enum, I think it would return a plain Enumerator instance, not the MyEnum with the specific feature built in. On top of that, I am not sure how to implement MyEnum in the first place. How can I implement such enumerator? Or, what is the right way to do this?
You could do something like this.
def foo a, &pr
if pr
a.map(&pr).join
else
o = Object.new
o.instance_variable_set :#a, a
def o.each *y
foo #a.map { |z| yield z, *y } { |e| e }
end
o.to_enum
end
end
Then we have
enum = foo([1,2,3])
enum.each { |x| 2 * x } # "246"
or
enum = foo([1,2,3])
enum.with_index { |x, i| x * i } # "026"
Inspiration was drawn from the Enumerator documentation. Note that all of your expectations about enumerators like you asked for hold, because .to_enum takes care of all that. enum is now a legitimate Enumerator!
enum.class # Enumerator

Are there something like Python generators in Ruby?

I am new to Ruby, is there a way to yield values from Ruby functions? If yes, how? If not, what are my options to write lazy code?
Ruby's yield keyword is something very different from the Python keyword with the same name, so don't be confused by it. Ruby's yield keyword is syntactic sugar for calling a block associated with a method.
The closest equivalent is Ruby's Enumerator class. For example, the equivalent of the Python:
def eternal_sequence():
i = 0
while True:
yield i
i += 1
is this:
def eternal_sequence
Enumerator.new do |enum|
i = 0
while true
enum.yield i # <- Notice that this is the yield method of the enumerator, not the yield keyword
i +=1
end
end
end
You can also create Enumerators for existing enumeration methods with enum_for. For example, ('a'..'z').enum_for(:each_with_index) gives you an enumerator of the lowercase letters along with their place in the alphabet. You get this for free with the standard Enumerable methods like each_with_index in 1.9, so you can just write ('a'..'z').each_with_index to get the enumerator.
I've seen Fibers used in that way, look at an example from this article:
fib = Fiber.new do
x, y = 0, 1
loop do
Fiber.yield y
x,y = y,x+y
end
end
20.times { puts fib.resume }
If you are looking to lazily generate values, #Chuck's answer is the correct one.
If you are looking to lazily iterate over a collection, Ruby 2.0 introduced the new .lazy enumerator.
range = 1..Float::INFINITY
puts range.map { |x| x+1 }.first(10) # infinite loop
puts range.lazy.map { |x| x+1 }.first(10) # [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Ruby supports generators out of the box using Enumerable::Generator:
require 'generator'
# Generator from an Enumerable object
g = Generator.new(['A', 'B', 'C', 'Z'])
while g.next?
puts g.next
end
# Generator from a block
g = Generator.new { |g|
for i in 'A'..'C'
g.yield i
end
g.yield 'Z'
}
# The same result as above
while g.next?
puts g.next
end
https://ruby-doc.org/stdlib-1.8.7/libdoc/generator/rdoc/Generator.html
Class Enumerator and its method next behave similar
https://docs.ruby-lang.org/en/3.1/Enumerator.html#method-i-next
range = 1..Float::INFINITY
enumerator = range.each
puts enumerator.class # => Enumerator
puts enumerator.next # => 1
puts enumerator.next # => 2
puts enumerator.next # => 3

passing functions as arguments in ruby

I'm trying to wrap my head around functional programming in ruby and there doesn't seem to be much good documentation out there.
Essentially, I'm trying to write a combine function that would have a Haskell type signature of:
[a] -> [a] -> (a -> a -> a) -> [a]
So
combine([1,2,3], [2,3,4], plus_func) => [3,5,7]
combine([1,2,3], [2,3,4], multiply_func) => [2,6,12]
etc.
I found some stuff about using zip and map but that feels really ugly to use.
What would be the most "ruby" way of implementing something like this?
Well, you said you know about zip and map so this probably isn't helpful.
But I'll post just in case.
def combine a, b
a.zip(b).map { |i| yield i[0], i[1] }
end
puts combine([1,2,3], [2,3,4]) { |i, j| i+j }
No, I don't find it beautiful either.
edit - #ruby-lang # irc.freenode.net suggests this:
def combine(a, b, &block)
a.zip(b).map(&block)
end
or this, if you want to forward args:
def combine(a, b, *args, &block)
a.zip(b, *args).map(&block)
end
A very naive aproach:
def combine(a1, a2)
i = 0
result = []
while a1[i] && a2[i]
result << yield(a1[i], a2[i])
i+=1
end
result
end
sum = combine([1,2,3], [2,3,4]) {|x,y| x+y}
prod = combine([1,2,3], [2,3,4]) {|x,y| x*y}
p sum, prod
=>
[3, 5, 7]
[2, 6, 12]
And with arbitrary parameters:
def combine(*args)
i = 0
result = []
while args.all?{|a| a[i]}
result << yield(*(args.map{|a| a[i]}))
i+=1
end
result
end
EDIT: I upvoted the zip/map solution, but here's a little improvement, what's ugly about it?
def combine(*args)
args.first.zip(*args[1..-1]).map {|a| yield a}
end
sum = combine([1,2,3], [2,3,4], [3,4,5]) {|ary| ary.inject{|t,v| t+=v}}
prod = combine([1,2,3], [2,3,4], [3,4,5]) {|ary| ary.inject(1){|t,v| t*=v}}
p sum, prod
You sound like you might also want Symbol.to_proc (code by Raganwald)
class Symbol
# Turns the symbol into a simple proc, which is especially useful for enumerations.
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
Now you can do:
(1..100).inject(&:+)
Disclaimer: I am not a Rubyist. I just like functional programming. So this is likely to be un-Ruby-like.
You can pass the name of the method as a symbol and use Object#send (or Object#__send__) to call it by name. (Ruby doesn't really have functions, it has methods.)
You can pass a lambda or block which calls your desired method on your desired arguments. Passing blocks is probably the preferred Ruby way, when it works (i.e. when you only have a single block to pass).
You retrieve Method objects directly via Object#method and then pass them around and call them, but I have little experience doing it this way and haven't seen it done much in practice.

Resources