Hashes vs Lambdas - ruby

I found two examples that looked close to each other for finding Fibonacci numbers:
Lambda
fibonacci = ->(x){ x < 2 ? x : fibonacci[x-1] + fibonacci[x-2] }
fibonacci[6] # => 8
Hash
fibonacci = Hash.new{ |h,x| h[x] = x < 2 ? x : h[x-1] + h[x-2] }
fibonacci[6] # => 8
I used both hashes and lambdas in ruby before, but not like this. This is more of a way of storing a function:
if x < 2
x
else
fibonacci[x-1] + fibonacci[x-2]
Can you explain in detail how this is working? Is this using recursion?
What are the differences between hashes like this and lambdas?

Yes it is using recursion. If we look at the code in the {}-brackets we can figure out the answer. Let's start looking at the hash. The values after new keyword is the default value. A value that will be assigned if the value does not already exist in the hash.
hash = Hash.new
p hash['new_value'] #=> nil
default_value_hash = Hash.new(0)
puts default_value_hash['new_value'] #=> 0
hash_with_block = Hash.new{|h,x| x}
puts hash_with_block['new_value'] #=> 'new_value'
So when we declare
fibonacci = Hash.new{ |h,x| h[x] = x < 2 ? x : h[x-1] + h[x-2] }
we are basically saying - Create a new hash with a default value. If we ask for a number (x) smaller or equal to two, just return the input (x). Else, give us the sum of the dictionary values where the key is x-1 and x-2. Basically the Fibonacci algorithm. If x-1 and x-2 does not exist, it runs the same code again until the two basic input values are 1 and 2.
The difference between the two approaches is that the hash saves the values (in a hash...). This can be a huge advantage in some cases. Every time the lambda is called it needs to recalculate the values for all numbers below the called value.
# Let's create a counter to keep track of the number of time the lambda is called.
# Please do not use global variables in real code. I am just lazy here.
#lambda_counter = 0
fibonacci_lambda = ->(x){
#lambda_counter += 1
x < 2 ? x : fibonacci_lambda[x-1] + fibonacci_lambda[x-2]
}
p (1..20).map{|x| fibonacci_lambda[x]}
# => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
p #lambda_counter # => 57290
# lambda called 57290 times!
#hash_counter = 0
fibonacci_hash = Hash.new{ |h,x|
#hash_counter += 1
h[x] = x < 2 ? x : h[x-1] + h[x-2]
}
p (1..20).map{|x| fibonacci_hash[x]}
# => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
p #hash_counter # => 21
# Only called 21 times!
The reason for the big difference in calls is the nature of recursion. The lambda does not store its values and when the value for 10 is calculated it recalculates the value for 3 more than 20 times. In the hash this value can be stored and saved for later.

In the first case, you are defining a recursion which will be called recursively.
In the case of the hash, the values will also be computed recursively, but stored and then access for giving the result.
Lambda
fibonacci = ->(x){ x < 2 ? x : fibonacci[x-1] + fibonacci[x-2] }
fibonacci[6]
fibonacci # => <Proc:0x2d026a0#(irb):5 (lambda)>
Hash
fibonacci = Hash.new{ |h,x| h[x] = x < 2 ? x : h[x-1] + h[x-2] }
fibonacci[6]
fibonacci # => {1=>1, 0=>0, 2=>1, 3=>2, 4=>3, 5=>5, 6=>8}
In one case, you are not leaving any footprint in memory, whereas the hash will continue to keep the computed value. So it depends on what you need.
If you need to access fibonacci[6] one more time, the lambda will recompute the result, whereas the hash will give you the result immediately without redo the calculations.

What are the differences between hashes like this and lambdas?
lambdas and hashes have nothing in common. Your question is like asking:
What are the differences between methods and arrays?
It's just that Hashes can specify a default value for a non-existent key:
h = Hash.new(10)
h["a"] = 2
puts h["a"]
puts h["b"]
--output:--
2
10
Hashes also provide a way to dynamically specify the default value: you can provide a block. Here is an example:
h = Hash.new do |h, key|
h[key] = key.length
end
puts h['hello']
puts h['hi']
p h
--output:--
5
2
{"hello"=>5, "hi"=>2}
When you access a non-existent key, the block is called, and the block can do whatever you want. So someone cleverly figured out that you could create a hash and specify a default value that calculated fibonacci numbers. Here is how it works:
h = Hash.new do |h, key|
if key < 2
h[key] = key
else
h[key] = h[key-1] + h[key-2]
end
end
That creates the Hash h, which is a Hash with no keys or values. If you then write:
puts h[3]
...3 is a non-existent key, so the block is called with the args h and 3. The else clause in the block executes, which gives you:
h[3-1] + h[3-2]
or:
h[2] + h[1]
But to evaluate that statement, ruby has to first evaluate h[2]. But when ruby looks up h[2] in the hash, the key 2 is a non-existent key, so the block is called with the args h and 2, giving you:
(h[2-1] + h[2-2]) + h[1]
or:
(h[1] + h[0]) + h[1]
To evaluate that statement, ruby first has to evaluate the first h[1], and when ruby tries to look up h[1] in the hash, 1 is a non existent key, so the block is called with the args h and 1. This time the if branch executes, causing this to happen:
h[1] = 1
and 1 is returned as the value of h[1], giving you this:
(1 + h[0]) + h[1]
Then ruby looks up h[0], and because 0 is a non-existent key, the block is called with the args h and 0, and the if clause executes and does this:
h[0] = 0
and 0 is returned as the value of h[0], giving you this:
(1 + 0) + h[1]
Then ruby looks up h[1] in the hash, and this time the key 1 exists, and it has a value of 1, giving you:
(1 + 0) + 1
And that is equal to 2, so h[3] is set equal to 2. After calling h[3], you get this output:
puts h[3]
p h
--output:--
2
{1=>1, 0=>0, 2=>1, 3=>2}
As you can see, the previous calculations are all cached in the hash, which means that those calculations don't have to be performed again for other fibonacci numbers.

Related

Generate a sequence in Ruby

I want to take an integer from the user, and generate a sequence of five numbers starting with the given integer, and multiplying each previous number by four, using iteration. E.g., if I enter 2 then the list should be [2, 8, 32, 128, 512].
n = gets
i = 0
while i < 4
n = n * 4
p n
i = i + 1
end
I am not sure if you are looking to create an array of numbers to use later or just want to iterate. In the following you can replace puts memo with whatever you want, memo will contain the value you are looking for each pass.
s = 2
(s...(s+5)).reduce(s) do |memo, i|
puts memo
memo = memo * 4
end
Run:
(5 - 1).times.with_object([gets.to_i]){|i, a| a.push(a.last * 4)}
Input:
2
Return value:
[2, 8, 32, 128, 512]
There are a lot of ways to accomplish what you are describing. You already have some implementations in the comments and in the other answer by #Abhimanyu.
the trouble I'm having is to iterate the value for the sequence. I keep getting it as [2,2,2,2,2]
Your code example won't work because Kernel#gets doesn't return a number, but a String followed by a newline character (because it uses a default separator, from the global variable $/, which happens to be "\n"):
[1] pry(main)> gets
2
=> "2\n"
[2] pry(main)> gets.class
2
=> String
To fix your snippet you'll need to add .chomp.to_i to remove the newline and to convert it to an integer:
def generate_sequence
n = gets.chomp.to_i
i = 0
while i < 4
n = n * 4
p n
i = i + 1
end
end

I don't understand this method

I'm a beginner in Ruby and I don't understand what this code is doing, could you explain it to me, please?
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
def defines a method. Methods can be used to run the same code on different values. For example, lets say you wanted to get the square of a number:
def square(n)
n * n
end
Now I can do that with different values and I don't have to repeat n * n:
square(1) # => 1
square(2) # => 4
square(3) # => 9
= is an assignment.
s = 0 basically says, behind the name s, there is now a zero.
0..n-1 - constructs a range that holds all numbers between 0 and n - 1. For example:
puts (0..3).to_a
# 0
# 1
# 2
# 3
for assigns i each consecutive value of the range. It loops through all values. So first i is 0, then 1, then ... n - 1.
s += i is a shorthand for s = s + i. In other words, increments the existing value of s by i on each iteration.
The s at the end just says that the method (remember the thing we opened with def) will give you back the value of s. In other words - the sum we accumulated so far.
There is your programming lesson in 5 minutes.
This example isn't idiomatic Ruby code even if it is syntactically valid. Ruby hardly ever uses the for construct, iterators are more flexible. This might seem strange if you come from another language background where for is the backbone of many programs.
In any case, the program breaks down to this:
# Define a method called a which takes an argument n
def a(n)
# Assign 0 to the local variable s
s = 0
# For each value i in the range 0 through n minus one...
for i in 0..n-1
# ...add that value to s.
s += i
end
# The result of this method is s, the sum of those values.
s
end
The more Ruby way of expressing this is to use times:
def a(n)
s = 0
# Repeat this block n times, and in each iteration i will represent
# a value in the range 0 to n-1 in order.
n.times do |i|
s += i
end
s
end
That's just addressing the for issue. Already the code is more readable, mind you, where it's n.times do something. The do ... end block represents a chunk of code that's used for each iteration. Ruby blocks might be a little bewildering at first but understanding them is absolutely essential to being effective in Ruby.
Taking this one step further:
def a(n)
# For each element i in the range 0 to n-1...
(0..n-1).reduce |sum, i|
# ...add i to the sum and use that as the sum in the next round.
sum + i
end
end
The reduce method is one of the simple tools in Ruby that's quite potent if used effectively. It allows you to quickly spin through lists of things and compact them down to a single value, hence the name. It's also known as inject which is just an alias for the same thing.
You can also use short-hand for this:
def a(n)
# For each element in the range 0 to n-1, combine them with +
# and return that as the result of this method.
(0..n-1).reduce(&:+)
end
Where here &:+ is shorthand for { |a,b| a + b }, just as &:x would be short for { |a,b| a.x(b) }.
As you are a beginner in Ruby, let's start from the small slices.
0..n-1 => [0, n-1]. E.g. 0..3 => 0, 1, 2, 3 => [0, 3]
for i in 0.. n-1 => this is a for loop. i traverses [0, n-1].
s += i is same as s = s + i
So. Method a(n) initializes s = 0 then in the for loop i traverse [0, n - 1] and s = s + i
At the end of this method there is an s. Ruby omits key words return. so you can see it as return s
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
is same as
def a(n)
s = 0
for i in 0..n-1
s = s + i
end
return s
end
a(4) = 0 + 1 + 2 + 3 = 6
Hope this is helpful.
The method a(n) calculates the sums of the first n natural numbers.
Example:
when n=4, then s = 0+1+2+3 = 6
Let's go symbol by symbol!
def a(n)
This is the start of a function definition, and you're defining the function a that takes a single parameter, n - all typical software stuff. Notably, you can define a function on other things, too:
foo = "foo"
def foo.bar
"bar"
end
foo.bar() # "bar"
"foo".bar # NoMethodError
Next line:
s = 0
In this line, you're both declaring the variable s, and setting it's initial value to 0. Also typical programming stuff.
Notably, the value of the entire expression; s = 0, is the value of s after the assignment:
s = 0
r = t = s += 1 # You can think: r = (t = (s += 1) )
# r and t are now 1
Next line:
for i in 0..n-1
This is starting a loop; specifically a for ... in ... loop. This one a little harder to unpack, but the entire statement is basically: "for each integer between 0 and n-1, assign that number to i and then do something". In fact, in Ruby, another way to write this line is:
(0..n-1).each do |i|
This line and your line are exactly the same.
For single line loops, you can use { and } instead of do and end:
(0..n-1).each{|i| s += i }
This line and your for loop are exactly the same.
(0..n-1) is a range. Ranges are super fun! You can use a lot of things to make up a range, particularly, time:
(Time.now..Time.new(2017, 1, 1)) # Now, until Jan 1st in 2017
You can also change the "step size", so that instead of every integer, it's, say, every 1/10:
(0..5).step(0.1).to_a # [0.0, 0.1, 0.2, ...]
Also, you can make the range exclude the last value:
(0..5).to_a # [0, 1, 2, 3, 4, 5]
(0...5).to_a # [0, 1, 2, 3, 4]
Next line!
s += i
Usually read aloud a "plus-equals". It's literally the same as: s = s + 1. AFAIK, almost every operator in Ruby can be paired up this way:
s = 5
s -= 2 # 3
s *= 4 # 12
s /= 2 # 6
s %= 4 # 2
# etc
Final lines (we'll take these as a group):
end
s
end
The "blocks" (groups of code) that are started by def and for need to be ended, that's what you're doing here.
But also!
Everything in Ruby has a value. Every expression has a value (including assignment, as you saw with line 2), and every block of code. The default value of a block is the value of the last expression in that block.
For your function, the last expression is simply s, and so the value of the expression is the value of s, after all is said and done. This is literally the same as:
return s
end
For the loop, it's weirder - it ends up being the evaluated range.
This example may make it clearer:
n = 5
s = 0
x = for i in (0..n-1)
s += i
end
# x is (0..4)
To recap, another way to write you function is:
def a(n)
s = 0
(0..n-1).each{ |i| s = s + i }
return s
end
Questions?

Finding the mode of an array in Ruby

When creating a method to find the mode of an array, I see people iterating over the array through a hash with default value 0:
def mode(array)
hash = Hash.new(0)
array.each do |i|
hash[i]+=1
end
end
or
freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
Can someone explain the following part of the block?
hash[i] = hash[i] + 1 or h[v] = h[v] + 1
How does the iterator know to add +1 to each unique key of the hash? For example:
array = [1,1,1,2,3]
freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
#=> {1:3, 2:1, 3:1}
If someone can explain how to find the mode of an array, I would be grateful.
In you first example, you need the method to return the hash that is created, or do some manipulation of the hash to compute the mode. Let's try it, just returning the hash (so I've added hash as the last line):
def hash_for_mode(array)
hash = Hash.new(0)
array.each do |i|
hash[i]+=1
end
hash
end
array = [1,3,1,4,3]
hash_for_mode(array) #=> {1=>2, 3=>2, 4=>1}
With hash_for_mode you can easily compute the mode.
By defining the hash h = Hash.new(0), we are telling Ruby that the default value is zero. By that, we mean that if a calculation is performed that depends on h[k] when k is not a key of h, h[k] will be set equal to the default value.
Consider, for example, when the first value of array (1 in my example) is passed into the block and assigned to the block variable i. hash does not have a key 1. (It has no keys yet.) hash[1] += 1 is shorthand for hash[1] = hash[1] + 1, so Ruby will replace hash[1] on the right side of the equality with the default value, zero, resulting in hash[1] => 1.
When the third value of array (another 1) is passed into the block, hash[1] already exists (and equals 1) so we just add one to give it a new value 2.
In case you were wondering, if we have:
hash = Hash.new(0)
hash[1] += 1
hash #=> {1=>1}
puts hash[2] #=> nil
hash #=> {1=>1}
That is, merely referencing a key that is not in the hash (here puts hash[2]), does not add a key-value pair to the hash.
Another common way to do the same thing is:
def hash_for_mode(array)
array.each_with_object({}) { |i,hash| hash[i] = (hash[i] || 0) + 1 }
end
hash_for_mode(array) #=> {1=>2, 3=>2, 4=>1}
This relies on the fact that:
(hash[i] || 0) #=> hash[i] if hash already has a key i
(hash[i] || 0) #=> 0 if hash does not have a key i, so hash[k]=>nil
(This requires that your hash does not contain any pairs k=>nil.)
Also, notice that rather than having the first statement:
hash = {}
and the last statement:
hash
I've used the method Enumerable#each_with_object, which returns the value of the hash. This is preferred here to using Enumerable#inject (a.k.a reduce) because you don't need to return hash to the iterator (no ; h needed).
array = [1,3,1,4,3]
array.group_by(&:itself).transform_values(&:count)
# => {1=>2, 3=>2, 4=>1}

Maximum and minimum value in an Array

I wrote a Ruby code to get max and min values from an array. The code prints the max value (8) correct but it's not printing the minimum value (2). Please let me know what went wrong in my code.
class MaxMinArray
def MaxMinMethod()
array = [4,2,8,3,5]
maxNo = array[0]
minNo = array[0]
arrayLength = array.length
for i in 1..arrayLength
if array[i].to_i > maxNo
maxNo = array[i]
end
if array[i].to_i < minNo
minNo = array[i]
end
end
puts "Maximum no. in the given array: " + maxNo.to_s
puts "Minimum no. in the given array: " + minNo.to_s
end
end
MaxiMinArrayObj = MaxMinArray.new
MaxiMinArrayObj.MaxMinMethod()
It is the combination of two things.
First, you iterated over for i in 1..arrayLength, which iterates past the last element in array. After the last element, array[i] is nil.
Second, you have the condition if array[i].to_i < minNo, which can be satisfied even if array[i] is not a number.
Because of that, the nil returned by array[i] after the last element satisfies the condition due to nil.to_i being 0, and that nil is assigned to minNo.
I realize you're trying to learn how to code, but, as you do so, it's also important to learn to take advantage of pre-existing solutions. Reinventing wheels will waste your time debugging code.
I'd write the code like:
def max_min(ary)
[ary.max, ary.min]
end
max_min([1,2,4]) # => [4, 1]
But, then again, Ruby already has a good minmax method:
[1,2,4].minmax # => [1, 4]
so use it and focus your energy on more interesting things.
If you have to see the values in the opposite order, use:
[1,2,4].minmax.reverse # => [4, 1]
A more verbose/old-school way of doing it is:
FIXNUM_MAX = (2 ** (0.size * 8 - 2) - 1)
FIXNUM_MIN = -(2 ** (0.size * 8 - 2))
def max_min(ary)
return [nil, nil] if ary.empty?
minval = FIXNUM_MAX
maxval = FIXNUM_MIN
ary.each do |i|
minval = i if i < minval
maxval = i if i > maxval
end
[maxval, minval]
end
max_min([1,2,4]) # => [4, 1]
[1,2,4].minmax.reverse # => [4, 1]
That simply loops over the array, checks each value to see if it's either smaller or larger than the last minimum or maximum value, and, if so, remembers it. Once the array is exhausted the values are returned. It's a lot more concise because using each removes a lot of the hassle of trying to walk the array using index values. We almost never use for in Ruby, especially to walk through an array.
(Technically Ruby can hold values well beyond 4611686018427387903 and -4611686018427387904, which are what FIXNUM_MAX and FIXNUM_MIN are, but those suffice for most things we want to do.)
It's not a good practice to print inside methods as long as you might want to use the results for something else.
Also Ruby comes with all sorts of magic methods to get the maximum and minimum of an array:
results = [5, 23, 43, 2, 3, 0].minmax
puts "Maximum no. in the given array: " + results[1]
puts "Minimum no. in the given array: " + results[0]
You should iterate from 1 to arrayLength - 1 (it's an index of the last element). You can use three dots for this:
for i in 1...arrayLength
If I were not allowed to used Ruby's minmax method, than I would do it probably like this:
array = [4,2,8,3,5]
min, max = nil, nil
array.each do |element|
min = element if min.nil? || element < min
max = element if max.nil? || max < element
end
puts [min, max]
# => [2, 8]
I used this expression for the min and max within ruby, it's a stretch but it works
class RubyMinMax
def self.min_function(array=[])
puts "my array is the following #{array}"
puts "the length of the array is #{array.length}"
it = 0
while array.length > 1
array.fetch(it).to_i > array.fetch(it-1).to_i ? array.delete_at(it) : array.delete_at(it-1)
it = array.length-1
end
print array[0]
end
def self.max_function(array=[])
puts "my array is the following #{array}"
puts "the length of the array is #{array.length}"
it = 0
while array.length > 1
array.fetch(it).to_i < array.fetch(it-1).to_i ? array.delete_at(it) : array.delete_at(it-1)
it = array.length-1
end
print array[0]
end
end
RubyMinMax.min_function([18, 19, 17])
RubyMinMax.max_function([18, 19, 17])
In the simplest way you can use max and min method of array.
:001 > [1,4,1,3,4,5].max
=> 5
:002 > [1,4,1,3,4,5].min
=> 1
And if your array may contain nil the first compact it the use min max
For example
:003 > [1,4,1,3,4,5,nil].compact
=> [1, 4, 1, 3, 4, 5]
:004 > [1,4,1,3,4,5].max
=> 5
:005 > [1,4,1,3,4,5].min
=> 1

Check if the sum of two different numbers in an array equal a variable number?

In Ruby, I would like to take an array of numbers, select 2 different numbers, add those 2 numbers together and see weather there equal to a variable x.y'd a variable x. Here is the code I used
def arrayIsEqual? (numArray, x)
return true if numArray.sample + numArray.sample == x
return false if numArray.empty? || numArray.count == 1
end
for example
numArray = [4,2,7,5]
x = 11
arrayIsEqual (numArray, n) should return true, since 4 + 7 = n(11)
How do I get this to work?
I don't want it to be 2 random numbers, just any 2 different numbers that add up to n
It looks like you're trying to see if there are any two numbers in the array that add up to the specified value x. However, your code just picks two numbers at random and checks if those numbers add up.
Ruby has the Array#combination method, which generates all combinations of a given length:
def contains_pair_for_sum?(arr, n)
!!arr.uniq.combination(2).detect { |a, b| a + b == n }
end
A few things to note:
First, we named it according to Ruby conventions: each word is separated_by_underscores. The ? on the end means that the method is a predicate method and returns a true or false value.
Inside the method, a few things happen. Let's look at that line, piece by piece.
arr: We take the array that was passed in.
<...>.uniq: We only look at the unique elements (because the OP wants to pick two different numbers).
<...>.combination(2): We ask for all combinations from the array of length 2. If the array was [4, 5, 6], we'd get [[4, 5], [4, 6], [5, 6]].
<...>.detect { |a, b| a + b == n }: We look for the first combination that adds up to n. If we found one, that's the result of that method. Otherwise, we get nil.
!!<...>: Finally, we take the result we got from detect and negate it twice. The first negation produces a Boolean value (true if the value we got was nil, or false if it's anything else); the second negation produces a Boolean value that's identical to the truth value of the first negation. This is a Ruby idiom to coerce a result into being either true or false.
Let's see it in action:
array = [4, 5, 9, 7, 8]
contains_pair_for_sum?(array, 11)
# => true (because [4, 7] sums to 11)
contains_pair_for_sum?(array, 17)
# => true (because [9, 8] sums to 17)
contains_pair_for_sum?(array, 100)
# => false (no pair matched)
I understand that your question is "is there any pair of numbers in my array equals x", in which case this will do what you need:
def has_pair_equal?(num_array, x)
(0..num_array.length-1).any? do |i|
num_array[i+1..-1].any? { |n| n + num_array[i] == x }
end
end
This checks all sums of pairs of numbers in the array, and checks if their sum is x. sample randomly picks an item from the array, which means that what your code does is "return true sometimes if there is a pair of numbers in my array equals x"
def array_is_equal? (num_array, x)
equality = 0
num_array.each do |a|
equality += 1 if a == x
return true if equality == 2
end
return false
end
Use lowercase and underscores for variables in Ruby. The convention is different here than in some other languages.
One liner
x=[4,2,7,5]; x.each_with_index.any? {|y,i| x.each_with_index.any? {|z,j| unless i==j; z+y==11; end } }
And as a function
def pair_sum_match?(arr, x)
arr.each_with_index.any? do |y,i|
arr.each_with_index.any? do |z,j|
unless i==j
z+y==x
end
end
end
end
Updated: Added each_with_index to avoid self inclusion on checks. It's a lot longer now :-/
Just iterate over it once and use the target number to see if it matches. 100 times faster then most of the answers here
numbers = ( -10..10 ).to_a
numbers.unshift( numbers.first + -1 ) # if you do -20 or 20
numbers.push( numbers.last + 1 )
target = 5
searched = { }
matches = { }
numbers.each do |number|
if searched[ target - number + 1 ] == true
matches[ "#{ number }_plus_#{ target - number }" ] = target
end
searched[ number + 1 ] = true
end
ap matches

Resources