Behaviour of each iterator in ruby - ruby

Why does this block of code output [1, 2, 3, 4, 5] and not [2, 3, 4, 5, 6]?
x = [1, 2, 3, 4, 5]
x.each do |a|
a + 1
end
I viewed the source of each at https://ruby-doc.org/core-2.2.0/Array.html#method-i-each. Something like this is written there.
VALUE
rb_ary_each(VALUE array)
{
long i;
volatile VALUE ary = array;
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(RARRAY_AREF(ary, i));
}
return ary;
}
Can someone please explain?

It outputs the object, you're calling each on, because this is the return value of each.
If you want to just print the a + 1, you should actually make it being output:
x.each do |a|
puts a + 1
end
Or, if your desired result is [2, 3, 4, 5, 6] - you want Enumerable#map, not each.
x.map { |a| a + 1 }
#=> [2, 3, 4, 5, 6]

Let me go through the key lines.
From this one it follows 'ary' is logically equal to array. Note the line is absent from newer versions of Ruby such as 2.4.0!
volatile VALUE ary = array;
I skip RETURN_SIZED_ENUMERATOR since a block is given. Refer to its source in at include/ruby/intern.h.
Next, we go into a 'for' for each element of 'ary' array.
Next is the line that puzzles you, I believe. First, it take i-th element from 'ary' array via RARRAY_AREF macro. Second, it passed the element's value to the block given (i.e 'a + 1') via rb_yield. Thus, it does not store anything.
rb_yield(RARRAY_AREF(ary, i));
Since nothing was written at rb_yield, the function return the 'ary' array which is [see above] is input 'array'.
Comparing it to 'map!' may help you further:
static VALUE rb_ary_collect_bang(VALUE ary)
{
long i;
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
rb_ary_modify(ary);
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_store(ary, i, rb_yield(RARRAY_AREF(ary, i)));
}
return ary;
}
Note 'rb_ary_store' function call inside the 'for' loop. It is the thing! It rb_yield-s just like in 'each' variant, but it does not throw away the result returned. The result is stored at i-th element of our [beloved] 'ary' array.

Related

Iterate over an array and initialize multiple variables in one line in Ruby

I am trying to iterate over an array and count the number of positive, negative and zeros in an array. Right now I am doing it like this
arr = [1, -1, 0, 2, 3, -2, -5]
pos = arr.select { |i| i > 0 }.count
neg = arr.select { |i| i < 0 }.count
zero = arr.select { |i| i == 0 }.count
puts pos
puts neg
puts zero
But is there any way where I can do this in one line? Something like this?
pos, neg, zero = arr.select { |i| i > 0; i < 0; i == 0; }.count
Use inject and the <=> operator:
neg, zero, pos = arr.inject([0,0,0]) { |a,b| a[(b<=>0)+1] += 1; a }
Alternatively, as #HolgerJust mentioned:
neg, zero, pos = arr.each_with_object([0,0,0]) { |a,b| b[(a<=>0)+1] += 1 }
is slightly longer but doesn't have the extra ; a in the block.
Inspired by #steenslag's use of tally:
neg, zero, pos = arr.map { |x| x<=>0 }.tally.values_at(-1,0,1)
If you use a counting hash the code is short and the results are returned in a hash, which may be convenient.
arr = [1, -1, 0, 2, 3, -2, -5, 4]
You could write
arr.each_with_object(Hash.new(0)) { |n,h| h[n<=>0] += 1 }
#=> {1=>4, -1=>3, 0=>1}
or perhaps you would prefer
labels = { -1=>:neg, 0=>:zero, 1=>:pos }
arr.each_with_object(Hash.new(0)) { |n,h| h[labels[n<=>0]] += 1 }
#=> {:pos=>4, :neg=>3, :zero=>1}
the last line of which could alternatively be written
arr.each_with_object({}) { |n,h| h[labels[n<=>0]] = (h[labels[n<=>0]] ||= 0) + 1 }
See Hash::new, specifically the (second) form that takes an argument called the default value (here zero), and no block. If a hash is defined h = Hash.new(0), then if h has no key k, h[k] returns 0 (and h is not changed).
arr = [1, -1, 0, 2, 3, -2, -5]
neg, zero, pos = arr.map{|n| n <=> 0}.tally.values_at(-1, 0, 1)
Using the new tally method.
As others have already said, you should just use inject and count using the <=> operator. If you plan to use similar logic frequently, you could monkey patch a #tally_by method into Enumerable like so:
class Enumerable
def my_tally(*keys, &proc)
proc ||= -> e {e} # Default identity proc
h = keys.empty? ? Hash.new(0) : Hash[keys.map{|k|[k, 0]}]
inject(h){|a, e| a[proc.call(e)] += 1; a}
end
end
This allows you to write:
neg, zero, pos = arr.my_tally(-1, 0, 1){|e| e <=> 0}
While this is certainly more upfront code than the others, it may be nice to have if you find yourself using similar logic frequently. You could also just make this a regular method somewhere if you don't like monkey-patching.

Decompose string to form a valid expression

I am given a string S (of integers) and a number N. I want to insert arbitrary number of '+' in S so that the sum becomes equal to N.
Ex:<br>
S = 15112 and N = 28<br>
Ans is : 15+11+2<br>
S = 120012 and N = 33<br>
Ans is : 1+20+012<br>
S = 123 and N = 123<br>
Ans is : 123
given : |S| <= 120 and N <= 10^6
It is guarenteed that S and N are given such that it is always possible to form valid expression. Is there any algorithm which can solve this? I tried to think on it but couldn't come up with solution.
There may be more efficient ways to do this, but since you have nothing so far…
You can simply find all combinations of a boolean array that indicates whether a plus should exist between the numbers or not.
For example: with an input of 112134, 1 + 12 + 13 + 4 can be represented with the boolean array [true, false, true, false, true] indicating that there is a plus after the 1st, 3rd, and 5th numbers. The problem then reduces to finding which combinations add to your number. There are lot of ways to find combinations. Recursive backtracking is a classic.
In javascript/node this might look like this:
function splitOnIndexes(arr, a) {
// split the array into numbers based on the booleans
let current = "" + arr[0]
let output = []
for (let i = 0; i < a.length; i++) {
if (!a[i]) {
current += arr[i + 1]
} else {
output.push(current)
current = "" + arr[i + 1]
}
}
output.push(current)
return output
}
function findSum(input, total) {
function backtrack(n, k = 0, a = []) {
const sum = (arr) => arr.reduce((a, c) => a + parseInt(c), 0)
if (k === n) {
let ans = splitOnIndexes(input, a)
if (sum(ans) === total) {
console.log(ans.join(' + '))
}
} else {
k = k + 1
let c = [true, false]
for (let i = 0; i < 2; i++) {
a[k - 1] = c[i]
backtrack(n, k, a)
}
}
}
backtrack(input.length - 1)
}
findSum('15112', 28)
findSum('120012', 33)
findSum('123', 123)
As you can see, more than one answer is possible. Your first example is solved with both 15+1+12 and 15+11+2. If you only need one, you can of course stop early.
The idea is to use dynamic programming, you only care about sums between 0 and 10^6 and only have 120 possible indexes. if dp[i][j] = x, it means that from index x of the string, we went to index i (so we added a + before i) and we got a sum of j. This leads to a O(|S| * N) solution:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
string s;
long n;
long dp[123][1000001];
void solve (int index, long sum) {//index = what index of s still remains to scan. sum = the sum we have accumulated till now
if (sum >= n or index >= s.length()) return;
if (dp[index][sum] != -1) return;
if (index == n and sum == n) return;
long num = 0;
for (int i = 0; i < 7 && index + i < s.length(); i++) { //N has 6 digits at most
num = stoi(s.substr(index, i + 1));
solve(index + i + 1, sum + num);
if (sum + num <= n) {
dp[index + i + 1][sum + num] = index;
}
}
}
int main () {
cin >> s;
cin >> n;
for (int i = 0; i < 121; i++) {
for (int j = 0; j < 1000001; j++) {
dp[i][j] = -1;
}
}
solve(0, 0);
int sum = n;
int idx = s.length();
vector<string> nums;
//reconstruct solution
while (idx != 0) {
nums.push_back(s.substr(dp[idx][sum], idx - dp[idx][sum]));
idx = dp[idx][sum];
sum -= stoi(nums[nums.size() - 1]);
}
for (int i = nums.size() -1; i >= 0; i--) {
cout << nums[i];
if (i != 0) cout << "+";
}
}
This is a Ruby version with step by step explanation of the algorithm, so you can easily code in C++ (or I'll try later).
# Let's consider that we extracted the values from text, so we already have the string of int and the result as integer:
string_of_int = "15112"
result = 28
# The basic idea is to find a map (array) that tells how to group digits, for example
sum_map = [2, 1, 2]
# This means that string_of_int is mapped into the following numbers
# 15, 1, 12
# then sum the numbers, in this case 15+1+12 = 28
# For finding a the solution we need to map
# all the possible combinations of addition given the n digits of the string_of_int then check if the sum is equal to the result
# We call k the number of digits of string_of_int
# in ruby we can build an array called sum_maps
# containing all the possible permutations like this:
k = string_of_int.length # => 5
sum_maps = []
k.times do |length|
(1..k).to_a.repeated_permutation(length).each {|e| sum_maps << e if e.inject(:+) == k}
end
sum_maps
# => [[1, 5], [2, 4], [3, 3], [4, 2], [5, 1], [1, 1, 4], [1, 2, 3], [1, 3, 2], [1, 4, 1], [2, 1, 3], [2, 2, 2], [2, 3, 1], [3, 1, 2], [3, 2, 1], [4, 1, 1]]
# Now must check which of of the sum_map is giving us the required result.
#
# First, to keep the code short and DRY,
# better to define a couple of useful methods for the String class to use then:
class String
def group_digits_by(sum_map)
string_of_int_splitted = self.split("")
grouped_digits = []
sum_map.each { |n| grouped_digits << string_of_int_splitted.shift(n).join.to_i}
grouped_digits.reject { |element| element == 0 }
end
def sum_grouped_of_digits_by(sum_map)
group_digits_by(sum_map).inject(:+)
end
end
# So we can call the methods directly on the string
# for example, in ruby:
string_of_int.group_digits_by sum_map #=> [15, 1, 12]
string_of_int.sum_grouped_of_digits_by sum_map #=> 28
# Now that we have this metods, we just iterate through the sum_maps array
# and apply it for printing out the sm_map if the sum of grouped digits is equal to the result
# coded in ruby it is:
combinations = []
sum_maps.each { |sum_map| combinations << string_of_int.group_digits_by(sum_map) if string_of_int.sum_grouped_of_digits_by(sum_map) == result }
p combinations.uniq
# => [[15, 1, 12], [15, 11, 2]]
In short, written as a Ruby module it becomes:
module GuessAddition
class ::String
def group_digits_by(sum_map)
string_of_int_splitted = self.split("")
grouped_digits = []
sum_map.each { |n| grouped_digits << string_of_int_splitted.shift(n).join.to_i}
grouped_digits.reject { |element| element == 0 }
end
def sum_grouped_of_digits_by(sum_map)
group_digits_by(sum_map).inject(:+)
end
end
def self.guess_this(string_of_int, result)
k = string_of_int.length
sum_maps = []
k.times { |length| (1..k).to_a.repeated_permutation(length).each {|e| sum_maps << e if e.inject(:+) == k} }
combinations = []
sum_maps.each { |sum_map| combinations << string_of_int.group_digits_by(sum_map) if string_of_int.sum_grouped_of_digits_by(sum_map) == result }
combinations.uniq
end
end
p GuessAddition::guess_this("15112", 28) # => [[15, 1, 12], [15, 11, 2]]

Nested if else inside .each iteration

I'm wondering if this makes sense or if the syntax is wrong and basically if this is acceptable. I wanted to nest an if/else condition within my iteration of the array.
def change_numbers(first_array, second_array)
second_array.each do |index|
if first_array[index] == 0
first_array[index] = 1
else
first_array[index] = 0
end
end
end
The array is a simple (binary) array and will only consist of 0s and 1s and I want to use the second array's elements as the indices of the first array that I am going to change.
Example:
first_array = [0, 0, 0, 0, 1, 1, 1, 1, 1]
second_array = [3, 5, 7]
Result:
first_array = [0, 0, 0, 1, 1, 0, 1, 0, 1]
If you don't want to use an if/else you can do:
second_array.each do |index|
first_array[index] = (first_array[index] + 1) % 2
end
def change_numbers(first_array, second_array)
second_array.each { |index| first_array[index] = 1 - first_array[index] }
end
A bit-wise XOR:
ar = [0, 0, 0, 0, 1, 1, 1, 1, 1]
indices = [3, 5, 7]
indices.each{|i| ar[i] ^= 1 }
You can try this -
def change_numbers(first_array, second_array)
second_array.each do |index|
first_array[index] = ((first_array[index] == 0) ? 1 : 0)
end
end

Ruby: reuse value in a block without assigning it to variable (write object method on the fly)

There are several situations where I'd like to apply a block to a certain value and use the value inside this block, to use the enumerator coding style to every element.
If such method would be called decompose, it would look like:
result = [3, 4, 7, 8].decompose{ |array| array[2] + array[3] } # result = 15
# OR
result = {:key1 => 'value', :key2 => true}.decompose{ |hash| hash[:key1] if hash[:key2] } # result = 'value'
# OR
[min, max] = [3, 4, 7, 8].decompose{ |array| [array.min, array.max] } # [min, max] = [3, 8]
# OR
result = 100.decompose{ |int| (int - 1) * (int + 1) / (int * int) } # result = 1
# OR
result = 'Paris'.decompose{ |str| str.replace('a', '') + str[0] } # result = 'PrisP'
The method simply yields self to the block, returning the block's result. I don't think it exists, but you can implement it yourself:
class Object
def decompose
yield self
end
end
[3, 4, 7, 8].decompose{ |array| array[2] + array[3] }
#=> 15
{:key1 => 'value', :key2 => true}.decompose{ |hash| hash[:key1] if hash[:key2] }
#=> "value"
[3, 4, 7, 8].decompose{ |array| [array.min, array.max] }
#=> [3, 8]
It actually exists (I could not believe it didn't).
It is called BasicObject#instance_eval. Here's the doc: http://apidock.com/ruby/BasicObject/instance_eval
Available since Ruby 1.9 as this post explains: What's the difference between Object and BasicObject in Ruby?

Bubble Sort Two Dimensional Array

I've been implementing an exercise on bubbling sorting.
So far, managed to bubble sort through an array.
Let's say, we have our array = ([5,6,7], [2,3,4])
How can I bubble sort through this without modifying the original array or using sort?
The exercise requires that I do not copy/duplicate/clone/edit or use a sort method.
My code to bubble sort through a regular array:
def bubble_sort(array)
is_sorted = false
until is_sorted
is_sorted = true
(array.count - 1).times do |i|
if array[i] > array[i + 1]
array[i], array[i + 1] = array[i + 1], array[i]
is_sorted = false
end
end
end
end
arr
As usual I learned quite a lot solving the OPs homework :-)
Firstly "do not copy/duplicate/clone/edit or use a sort method" just means that you have to implement your own algorithm. It does not mean you shouldn't copy the array passed to your function. You must copy your original array to avoid the side effect of modifying the original array.
However! if you simply copy the array new_array = array ruby will copy by reference.
That means that both variables will still point to the same data structure. Your sort will happily sort the numbers and the array passed as a parameter will be modified.
To fix this you need to copy the array using the dup function.
new_array = array.dup
Then the bubble sort function ends up looking like this:
def bubble_sort(array)
result = array.dup
begin
sorted = true
(0..result.length - 2).each do |i|
if result[i] > result[i + 1]
result[i], result[i+1] = result[i+1], result[i]
sorted = false
end
end
end while !sorted
result
end
Some test code:
a = [9,8,7,6,5,5,9,]
puts "array before sorting:"
puts a.inspect
puts "sorted array returned by function"
puts bubble_sort(a).inspect
puts "original array after sorting:"
puts a.inspect
Resulting output:
>ruby mysort.rb
array before sorting:
[9, 8, 7, 6, 5, 5, 9]
sorted array returned by function
[5, 5, 6, 7, 8, 9, 9]
original array after sorting:
[9, 8, 7, 6, 5, 5, 9]
Create a new array that is a copy of the original and sort that. That will let you keep the original array and have the sorted array.
Can you just combine the two arrays into a single array and then run a typical bubble sort?
def combine_arrays(arr1,arr2)
final = arr1 + arr2
sorted = true
while sorted do
sorted = false
(0..final.length - 2).each do |x|
if final[x] > final[x+1]
final[x], final[x+1] = final[x+1], final[x]
sorted = true
end
end
end
final
end
p combine_arrays([1,3,5],[2,4,6]) => [1, 2, 3, 4, 5, 6]
i think the simplest way for Bubble Sorting multi Dimensional Arrays is:
int main()
{
const size_t row = 4,col=6;
int temp;
int arr[row][col] = { { 6, 5, 4, 3, 2, 1 }, { 12, 11, 10, 9, 8, 7 }, {18, 17, 16, 15, 14, 13 }, { 24,23,22,21,20,1 } };
for (auto &row : arr)
{
for (int i = 0; i != col; ++i)
{
int flag = 0;
for (int j = 0; j != col-i - 1; ++j)
{
if (row[j]>row[j+1])
{
temp = row[j];
row[j] = row[j + 1];
row[j + 1] = temp;
flag = 1;
}
}
if (!flag)break;
}
}
for (auto &row : arr)
{
for (auto col : row)
cout << col << " ";
cout << endl;
}
return 0;
}

Resources