Defining algebra on Proc - ruby

I am trying to define conjunction Proc#* and disjunction Proc#+ on Proc class. When the receiver and the argument have different arity, then it should raise an error. Otherwise, it should return a proc object that is conjunction/disjunction, respectively. I implemented them as follows:
class Proc
def * other
raise ArgumentError, "Arity does not match" unless arity == other.arity
->(*args){call(*args) and other.call(*args)}
end
def + other
raise ArgumentError, "Arity does not match" unless arity == other.arity
->(*args){call(*args) or other.call(*args)}
end
end
This works well with simple procs:
p = ->x{x % 2 == 0} * ->x{x % 3 == 0}
p.call(2) # => false
p.call(6) # => true
but it raises an error when I further try to build on the result of these methods:
q = p * ->x{x % 5 == 0}
# => Error, Arity does not match
This is because the arity of ->x{x % 5 == 0} is 1, whereas the arity of p is -1 due to call(*args) in my implementation.
Is there any good way to make the methods Proc#* and Proc#+ work recursively?
If I remove the raise ... from the definition, then it will work, but then, it will return misleading error messages when procs with different arity are conjoined/disjoined. For example, suppose the raise ... part is deleted from the definition above, and I do:
p = ->x{x % 2 == 0} * ->x, y, z{x % 3 == 0}
then Proc#* will not raise an error, but will return a valid proc object. However, since part of it requires one argument and another part requires three arguments, there would be no way to pass arguments to p in a valid way.
p.call(2) # => error
will raise an ArgumentError, saying:
Wrong number of arguments
but the mistake actually happened when creating p that cannot be satisfied with any number of arguments, and the error message would be misleading. That is why I added the raise ... check. Is removing the raise ... only way to make it work?

I think you probably need to remove the raise because procs that take variable numbers of arguments can be validly called but have different arity values. For example
p = ->(a) {} * (a, *args) {}
p.call(1) # valid
p.call(1,2) # invalid
Maybe a solution would be to improve the error message within the returned lambda? Something like
lambda(*args) do
raise "wrong number of arguments for left lambda" if (arity < 0 && arity.abs - 1 <= args.size) || (arity > 0 && arity != args.size)
# etc (not sure those cases are exactly right)
call(*args) and other.call(*args)
end
It may be easier to use the parameters method, which will spell out the required, optional and splat parameters. Should make it easy to check if it will never be possible to call the resulting lambda and throw an error on creation in that case.

One possible way is to extend Proc:
class MyProc < Proc
attr_reader :my_arity
def initialize(my_arity = nil, &block)
#my_arity = my_arity || block.arity
super(&block)
end
def *(other)
other = MyProc.new(&other)
raise "Arity error" unless my_arity == other.my_arity
MyProc.new(my_arity, &->(*args){ call(*args) && other[*args] })
end
end
p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x, y){ x == y })[1,1] #=> true
p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x, y){ x != y })[1,1] #=> false
p (MyProc.new(&->(x, y){ x == y }) * ->(x, y){ x == y } * ->(x){ x })[1,1] #=> exception

Related

Passing a condition as a Proc/Lambda/Block in recursive function causes it to exit once the condition inside the lambda has evaluated to true. Why?

So I want to create a recursive function that, when given an array, will return an array of elements that meet the condition passed into the function. I have tried using lambdas, blocks and procs, and every time they exit the function once that condition evaluates to true, rather than when the base case is met. I want to know why this is and how I can overcome it.
def find_by_condition_recur(arr, count, acc, &block)
return acc if count == arr.count - 1
puts count
puts arr.count
if block.call(arr[count])
acc << arr[count]
else
find_by_condition_recur(arr, count += 1, acc, &block)
end
end
EDIT:
def find_by_condition_recur(arr, findBy, count, acc)
return acc if count == arr.count - 1
puts count
puts arr.count
if findBy.call(arr[count])
acc << arr[count]
find_by_condition_recur(arr, findBy, count += 1, acc)
end
end
search_condition = Proc.new { |x| x % 3 == 0 }
Here is some working code for you:
def find_by_condition_recur(arr, idx=0, acc=[], &findBy)
return acc if idx == arr.count
if findBy.call(arr[idx])
acc << arr[idx]
end
find_by_condition_recur(arr, idx + 1, acc, &findBy)
end
find_by_condition_recur([1,2,3]) { |num| num != 2 }
# => [1,3]
There were two major fixes:
The base case is idx == arr.count, not idx == arr.count - 1. You only want to return here if the idx is out-of-bounds - arr.count - 1 is in bounds, so if you return in that case, you would skip the last iteration.
You need to move the final find_by_condition_recur call to outside of the if findBy.call block, otherwise recursion will stop as soon as the condition fails (and the rest of the array won't be processed).
Other than that there were a few refactorings I made:
Default values for arguments. There's no reason to have to specify idx or acc arguments the first time the method is called - we know are they are going to be 0 and [] respectively so let's just set those as the default values.
Block should always be the last argument, otherwise you are forcing the caller to use an ugly proc/lambda literal. It's much more idiomatic to use blocks, and you can only use blocks when it's the last argument.
Using idx instead of count as the variable name, this is just more accurate.

How to optimize code - it works, but I know I'm missing much learning

The exercise I'm working on asks "Write a method, coprime?(num_1, num_2), that accepts two numbers as args. The method should return true if the only common divisor between the two numbers is 1."
I've written a method to complete the task, first by finding all the factors then sorting them and looking for duplicates. But I'm looking for suggestions on areas I should consider to optimize it.
The code works, but it is just not clean.
def factors(num)
return (1..num).select { |n| num % n == 0}
end
def coprime?(num_1, num_2)
num_1_factors = factors(num_1)
num_2_factors = factors(num_2)
all_factors = num_1_factors + num_2_factors
new = all_factors.sort
dups = 0
new.each_index do |i|
dups += 1 if new[i] == new[i+1]
end
if dups > 1
false
else
true
end
end
p coprime?(25, 12) # => true
p coprime?(7, 11) # => true
p coprime?(30, 9) # => false
p coprime?(6, 24) # => false
You could use Euclid's algorithm to find the GCD, then check whether it's 1.
def gcd a, b
while a % b != 0
a, b = b, a % b
end
return b
end
def coprime? a, b
gcd(a, b) == 1
end
p coprime?(25, 12) # => true
p coprime?(7, 11) # => true
p coprime?(30, 9) # => false
p coprime?(6, 24) # => false```
You can just use Integer#gcd:
def coprime?(num_1, num_2)
num_1.gcd(num_2) == 1
end
You don't need to compare all the factors, just the prime ones. Ruby does come with a Prime class
require 'prime'
def prime_numbers(num_1, num_2)
Prime.each([num_1, num_2].max / 2).map(&:itself)
end
def factors(num, prime_numbers)
prime_numbers.select {|n| num % n == 0}
end
def coprime?(num_1, num_2)
prime_numbers = prime_numbers(num_1, num_2)
# & returns the intersection of 2 arrays (https://stackoverflow.com/a/5678143)
(factors(num_1, prime_numbers) & factors(num_2, prime_numbers)).length == 0
end

Counters inside a function

I'm trying to get a some counters inside a function that gets called repeatly.
In the function, gets is passed a value between 1 and 6 and then inside the function are a bunch of if statements. Inside the if statements I want to put a counter so I know how times each part of the if statement is true.
I've tried x = x+1 inside the if but it doesn't like that:
def check(number)
if number == 1
x = x+1
if x == 3
return true
end
elsif number == 2
y = y+1
elsif number == 3
z = z + 1
end
end
Any suggestions?
The pattern you're using here is a clunky way of doing this:
def check(number)
case (number)
when 1
#x += 1
true
when 2
#y += 1
when 3
#z += 1
end
end
Remember that in Ruby the last statement to evaluate is the one that's returned by default, so there's no need to be explicit about the return unless there's additional code after that point. In this case there isn't.
Secondly, unless you have attr_accessors, then x = x + 1 isn't going to do anything useful. That will declare a local, initially nil, and then try and add 1 to it, an invalid operation. Presumably you mean to have #x initialized to 0 elsewhere and then track it in here.
Local variables don't retain their values across different invocations of a method, that would be very weird.
If the counter values are semantically a property of the method itself, I would use a closure:
x = y = z = 0
define_method(:check) do |number|
case number
when 1 then x += 1; return true if x == 3
when 2 then y += 1
when 3 then z += 1
end
end
Alternatively, if the counter values are semantically a property of the object, I would use private getters and setters:
def check(number)
case number
when 1 then self.x += 1; return true if x == 3
when 2 then self.y += 1
when 3 then self.z += 1
end
end
private
attr_accessor :x, :y, :z
def initialize
self.x = self.y = self.z = 0
end

How do I perform variable assignment with a loop, and break?

Here is the logic:
y = 'var to check for'
some_var = some_loop.each do |x|
x if x == y
break if x
end
Is there a better way to write this?
Something like
x && break if x == y
Thank you in advance!
The correct answer is to use include?. eg:
found = (array_expression).include? {|x| x == search_value}
It's possible to also use each and break out on the first matched value, but the C implementation of include? is faster than a ruby script with each.
Here is a test program, comparing the performance of invoking include? on a very large array vs. invoking each on the same array with the same argument.
#!/usr/bin/env ruby
#
require 'benchmark'
def f_include a, b
b if a.include?(b)
end
def f_each_break a, b
a.each {|x| return b if x == b }
nil
end
# gen large array of random numbers
a = (1..100000).map{|x| rand 1000000}
# now select 1000 random numbers in the set
nums = (1..1000).map{|x| a[rand a.size]}
# now, check the time for f1 vs. f2
r1 = r2 = nil
Benchmark.bm do |bm|
bm.report('incl') { r1 = nums.map {|n| f_include a,n} }
bm.report('each') { r2 = nums.map {|n| f_each_break a,n} }
end
if r1.size != r2.size || r1 != r2
puts "results differ"
puts "r1.size = #{r1.size}"
puts "r2.size = #{r2.size}"
exceptions = (0..r1.size).select {|x| x if r1[x] != r2[x]}.compact
puts "There were #{exceptions.size} exceptions"
else
puts "results ok"
end
exit
Here is the output from the test:
$ ./test-find.rb
user system total real
incl 5.150000 0.090000 5.240000 ( 7.410580)
each 7.400000 0.140000 7.540000 ( 9.269962)
results ok
Why not:
some_var = (some_loop.include? y ? y : nil)

Lambdas and only 1 argument in case statements in Ruby

As I get it, you can use a proc/lambda inside, switch, for example:
is_even = ->(x) { x % 2 == 0 }
case number
when 0 then puts 'zero'
when is_even then puts 'even'
else puts 'odd'
end
As I understand it, and from the examples I see, is it that lambdas can be used in case statements ONLY IF they accept only 1 parameter, since you can't do a case statement with more than 1 argument (case a, b isn't possible, except if maybe these elements are enclosed into an array). So basically if you try to use a lambda with more than 1 parameter in a case statement the code is going to break. Correct me if I'm wrong.
It's because:
is_even = ->(x) { x % 2 == 0 }
is_even.lambda? #true
is "lambda" ( is_even = lambda(x) { x % 2 == 0 } is equivalent of above code)
There exist procs:
is_even = proc{|x| x % 2 == 0 } # Proc.new{|x| x % 2 == 0 }
is_even.lambda? #false
lambdas checks for number of arguments. procs on other hand doesn't check number of arguments.
ppp1 = Proc.new{|x| x % 2 == 0 } #=> #<Proc:0x507f300#(pry):152>
ppp1.call(2,3,4,5) #true
ppp2 = ->(x){ x % 2 == 0 } # => #<Proc:0x2c5ac40#(pry):158 (lambda)>
ppp2.(2,4) # ArgumentError: wrong number of arguments (2 for 1)
You can read more here.

Resources