ruby regular expression begin method a bit confusing - ruby

m = /(.)(.)(\d+)(\d)/.match("THX1138.")
puts m[0]
c = m.captures #=> HX1138
puts c[0] #=> H
puts m.begin(0) #=> 1
puts c[1] #=> X
puts m.begin(1) #=> 1
puts c[2] #=> 113
puts m.begin(2) #=> 2
I was expecting m.begin(1) to return 2 since X is two elements after the beginning of string.
I am reading the book well grounded rubyist which says
To get the information for capture n,
you provide n as the argument to begin
and/or end.
Similarly I was expecing m.begin(2) to rerturn 3.

Read carefully:
Returns the offset of the start of the nth element of the match array in the string.
So the match array is actually [HX1138,H,X,113,8]
SO
m.begin(0) => offset of HX1138 => 1 in "THX1138"
m.begin(1) => offset of H => 1 in "THX1138"
m.begin(2) => offset of X => 2 in "THX1138"

Related

What's Python's next() in Ruby?

I am currently learning Ruby and am lost a bit.
This Python snippet:
n = iter([1,2,3,4,5])
for x in n:
print(x)
y = next(n)
print(y)
gives:
1
2
3
4
5
And I'm trying to do the same in Ruby, which is not working:
n = [1,2,3,4,5].each
for x in n do
puts x
y = n.next()
puts y
end
How do I need to write the Python example in Ruby?
You have the right idea except Ruby keeps track of the position of the enumerator internally. Try this:
enum = [1,2,3,4,5].each
#=> #<Enumerator: [1, 2, 3, 4, 5]:each>
loop do
puts enum.next
puts enum.next
end
1
2
3
4
5
Enumerator#next raises a StopInteration exception when invoked after all elements of the enumerator enum have been generated. Kernel#loop handles the exception by breaking out of the loop.
Note that Array#each, without the optional block, has the same effect as Object#to_enum. Also, though authors of Ruby books feel an obligation to cover for loops (seemingly, on page one), they are never used in practice.
In Python the iterator n obtained applying the function iter() can be consumed once.
array = [1,2,3,4,5]
n = iter(array)
for x in n:
print(x)
# 1
# 2
# 3
# 4
# 5
If you then call next(n) you get no output but error: StopIteration. If you try to iterate n again you get no output (no error, the for loop hanldes the exception).
But you can iterate over the array:
for x in array:
print(x)
If you check the output a code like yours, you get:
n = iter([1,2,3,4,5])
for x in n:
print('odd_', x)
y = next(n)
print('even', y)
# odd_ 1
# even 2
# odd_ 3
# even 4
# odd_ 5
# StopIteration
As pointed by #toniedzwiedz Object#to_enum is the equivalent for the Python iter(), so you can call Enumerator#next on it.
In Ruby you can consume more than once the enumerator:
array = [1, 2, 3, 4, 5]
n = array.to_enum
# n = array.each # each returns an Enumerable
for x in n do
puts x
end
for x in n do
puts x
end
Since you can consume more than once the enumerator you get "double" output:
for x in n do
p x
p n.next
end
Or using Array#each:
n.each do |x| # same as array.each.each
p x
p n.next
end
Also in Ruby you reach end the iteration signal:
6.times do
p n.next
end
# `next': iteration reached an end (StopIteration)
You're just trying to print each element of the Array?
[1,2,3,4,5].each {|n| puts n}
If you're looking for a direct equivalent of Python's iterator, you should check out Enumerator#next for a very similar API
arr = [1, 2, 3].to_enum
#<Enumerator: [1, 2, 3]:each>
arr.next
# 1
for x in arr do
puts x
puts arr.next
end
#1
#2
#2
#3
#3
#StopIteration: iteration reached an end
each_cons is what you need. But this method has a downside that is the current will not print last element of the array. Please keep in mind this.
[1,2,3,4,5].each_cons(2) { |current,next_val| puts next_val }

Ruby map! does not change contained variable

Suppose I have an object x, and an array y=[x] which contains x. If I manipulate x, then y does not change:
x = 1 # => 1
y = [x] # => [1]
x = x+1 # => 2
x # => 2
y # => [1]
and if I change y, then x does not change.
x = 1 # => 1
y = [x] # => [1]
y.map!{|a| a+1} # => [2]
y # => [2]
x # => 1
Is there a way to have them change in parallel? It feels like when I map! over an array the underlying values should change.
first of all
x = x + 1
will create a new variable with an old name x
x = 1
y = [x]
x.object_id
# 3
y[0].object_id
# 3
x = x + 1
x.object_id
# 5
y[0].object_id
# 3
Second, numbers are immutable objects in Ruby (and Strings, for example, are mutable). So you just can't do what you want using number objects in Ruby. What you can do is slightly more obscure. You can create your own mutable object (container for a number) and use a reference to this object in your array.
class MutableNumber
attr_accessor :n
def initialize(n)
#n = n
end
end
x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x.n += 1
y[0].n
#=> 2
You can go little bit further and to add more magic here to mimic numbers
class MutableNumber
attr_accessor :n
def initialize(n)
#n = n
end
def method_missing(m, *args, &blk)
#n = #n.send(m, *args, &blk)
self
end
end
x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x += 1
y[0].n
#=> 2
But I would not encourage you to do any of this.
Just stick to the idea that numbers are immutable. And overall you should be careful with mutability.
map! is mutable for the array, not for its elements. It means that the array gets new values in place (i.e. it's assigned to original array). Elements inside the array are not mutated, but replaced by new elements.
If you want to change old values, you can iterate using each and call mutating methods on each element. You can see this with array of strings:
a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.map!{|e| e.capitalize }
#=> ["A", "B"]
[a, b]
#=> ["a", "b"]
a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.each{|e| e.capitalize! }
#=> ["A", "B"]
[a, b]
#=> ["A", "B"]
Note that it won't work for immutable objects and numbers are immutable, as #fl00r explained in his answer.

Unexpected keyword-else

I have a method which returns finds out which one of the given numbers differs from the others in evenness and returns the index of it (+1).
def iq_test(numbers)
new_array = numbers.split(" ").collect {|n| n.to_i}
x = new_array.select(&:even?)
y = new_array.select(&:odd?)
if x.count > y.count
new_array.split.each_with_index do |value, index|
"#{index + 1}".to_i if value % 3 != 0
else y.count > x.count
"#{index + 1}".to_i if value % 2 == 0
end
end
end
For example iq_test("2 4 7 8 10") should return 3.
However, I am receiving
syntax error, unexpected keyword_else, expecting keyword_end
and I can't find out where I am not closing some code off.
This is going to be part code review as well as answer. Let's start with the first part:
new_array = numbers.split(" ").collect {|n| n.to_i}
x = new_array.select(&:even?)
y = new_array.select(&:odd?)
Spend time to think of good variable names. I wouldn't use "new_array" as a variable name, because it doesn't provide any clue as to what it contains. Do this kind of thing too many times, and a program becomes incomprehensible.
x and y are really evens and odds, aren't they? Those would be better variable names.
A common Ruby idiom is to use plural names for arrays, singular names for everything else.
The split by default splits on whitespace, so the (" ") is unnecessary.
Be careful with indentation.
Your selects are fine, however there is a shortcut: Enumerable's partition.
This is really my own style, but I use map when processing all values in an array, and collect only when doing something like extracting attributes from an array of objects. (In other words, I use map much more often).
note that (&:to_i) is a bit of a shortcut for {|n| n.to_i}
Rewritten considering the above, this part might look like this:
numbers = input.split.map(&:to_i)
evens, odds = numbers.partition(&:even?)
Now let's look at the rest:
if x.count > y.count
new_array.split.each_with_index do |value, index|
"#{index + 1}".to_i if value % 3 != 0
else y.count > x.count
"#{index + 1}".to_i if value % 2 == 0
end
end
And let's consider the error message you got: unexpected keyword else; expected end. This has everything you need to know to answer the question (and you'll find that most error messages do, if you think about them). It says it found an else where it expected an end. And that's exactly the problem, you need to put end before the else to close the do/end block. Also, your else part is missing your iteration logic.
Other notes:
Again, be careful with indentation. Your ends do not line up with what they're ending. Proper alignment can help catch these kinds of errors. Use of an IDE like Rubymine or a sophisticated text editor with Ruby support can help as well.
else clauses are standalone, you don't put a condition after them. Perhaps you meant elsif as Holger commented.
Using string interpolation ("#{}") converts expressions to a string. Here you're converting index + 1 to a string, and then back to an integer with .to_i which cancels it out, so to speak. Simply index + 1 will do.
Array#index can be used to determine the index of a value.
It's not clear if you want all indices in case there is more than one.
Here's a version considering the above:
if evens.count > odds.count
odds.map{|n| numbers.index(n) + 1}
elsif odds.count > evens.count
evens.map{|n| numbers.index(n) + 1}
end
If you like this kind of thing, bring your working code to http://codereview.stackexchange.com/!
This specific error is because you do not have a closing end for your each_with_index block. To fix this error you need:
def iq_test(numbers)
new_array = numbers.split(" ").collect {|n| n.to_i}
x = new_array.select(&:even?)
y = new_array.select(&:odd?)
if x.count > y.count
new_array.split.each_with_index do |value, index|
"#{index + 1}".to_i if value % 3 != 0
end #end of block
elsif y.count > x.count #in order for this to have a condition, it must be an elsif
"#{index + 1}".to_i if value % 2 == 0
end #end of if statement
#end - remove this extra end
end #end of function
Code
def odd_one_out(str)
evens, odds = str.split.partition { |s| s.to_i.even? }
case [evens.size <=> 1, odds.size <=> 1]
when [0, 1] then evens.first
when [1, 0] then odds.first
else nil
end
end
Examples
odd_one_out "0 3 4 6" #=> "3"
odd_one_out "1 5 7 8" #=> "8"
odd_one_out "0 2 4 6" #=> nil
odd_one_out "1 5 7 9" #=> nil
odd_one_out "0 2 3 5" #=> nil
odd_one_out "3" #=> nil
odd_one_out "8" #=> nil
odd_one_out "" #=> nil
Explanation
See Integer#<=>.
Suppose
str = "0 3 4 6"
then
a = str.split
#=> ["0", "3", "4", "6"]
evens, odds = a.partition { |s| s.to_i.even? }
#=> [["0", "4", "6"], ["3"]]
evens
#=> ["0", "4", "6"]
odds
#=> ["3"]
b = [evens.size <=> 1, odds.size <=> 1]
#=> [1, 0]
b == [0, 1]
#=> false
b == [1, 0]
#=> true
c = odds.first
#=> "3"
The case statement, and therefore the method, returns "3".

Count Fixnums in Ruby array

I have a function with an arbitrary number of arguments. My function looks like this:
sum(n, *rest)
How do I iterate through the array rest so that it will check if an argument is a Fixnum, sum it if it is, and do nothing otherwise? For example:
sum(5,1,2,k,D,3)
# => 6
Do as below :
ary = rest.grep(Fixnum)
# return the summation of the array elements, if array is not empty
# 0, when array is empty or contains only one element as `0`.
ary.reduce(0,:+)
Look #reduce and grep .
Examples :
ary = [1,"2",{1 => 2 },8]
ary.grep(Fixnum).inject(0,:+) # => 9
ary = ["2",{1 => 2 },(1..2)]
ary.grep(Fixnum).inject(0,:+) # => 0
# counting the how many Fixnums are there
ary = [1,"2",{1 => 2 },8]
ary.grep(Fixnum).size # => 2
Another way (though I prefer #Arup's use of grep):
a.group_by(&:class)[Fixnum].reduce(0,:+)
a = [1, :cat, 3, 'dog']
b = a.group_by(&:class) #=> {Fixnum=>[1,3],Symbol=>[:cat],String=>["dog"]}
c = b[Fixnum] #=> [1,3]
reduce(0,:+) #=> 4
I have assumed that zero is to be returned if the array contains no Fixnums. If, in that case, nil is to be returned, change reduce(0,:+) to reduce(:+).

Ruby Logic comparisons with Nil

I am having trouble understanding the following code:
vowels_arr = ["a","e","i","o","u"]
(0...(vowels_arr.length - 1)).all? {|i| vowels_arr[i] <= vowels_arr[i + 1]}
When I try to run it WITHOUT the - 1, I get an error saying that I can't compare a string to nil. But what I dont understand is that why do we even need the -1?? The "..." ranger makes it so we are only evaluating "a","e","i","o" (4 out of the 5). Since the total length is 5 and we are already at 4 things to compare, my belief is that the comparison (vowels_arr[i] <= vowels_arr [i+1]) should work without the -1.
Can someone please explain to me why we need the -1 after array length?
Also are there other ways in ruby to get past this comparing to nil error?
It's because of this:
vowels_arr[i + 1]
(0...(vowels_arr.length)) will return all indexes for the array.
(0...(vowels_arr.length)).to_a # => [0, 1, 2, 3, 4]
But then you're trying to get next index from current. If current index is last index (4), this results in an error, because you get nil where you expect a string (because element doesn't exist at non-existent index). That's why you need length - 1, to allow your logic not to go out of array's bounds.
By the way, if you're trying to check if the array is sorted, why not do it more directly?
vowels_arr = ["a","e","i","o","u"]
puts vowels_arr.sort == vowels_arr
# >> true
As Sergio answers, the problem is with vowels_arr[i + 1]. The variable i ranges over the indices of vowels_arr, and hence i + 1 will not necessarily point to an existing index of vowels_arr. Particularly, when i reaches the last index, i + 1 will be greater than the existing indices, and vowels_arr[i + 1] will be nil.
Also as Sergio answers, if your purpose is to see if it is sorted, then doing as Sergio's answer is straightforward, but in general cases, you can do it like this:
vowels_arr.each_cons(2).all?{|e1, e2| e1 <= e2}
vowels_arr = ["a","e","i","o","u"]
p vowels_arr[vowels_arr.length] #=> nil
(0..(vowels_arr.length)).all? {|i| vowels_arr[i] <= vowels_arr[i + 1]}
#=> `<=': comparison of String with nil failed (ArgumentError)
As you are passing the vowels_arr[vowels_arr.length] element to the block,which is nil. In Ruby array's are 0(zero) based. Thus vowels_arr.length gives 5 means elements are in the range of (0..4). see below:
vowels_arr = ["a","e","i","o","u"]
p vowels_arr[0] #=> "a"
p vowels_arr[1] #=> "e"
p vowels_arr[2] #=> "i"
p vowels_arr[3] #=> "o"
p vowels_arr[4] #=> "u"
p vowels_arr[5] #=> nil
p vowels_arr[6] #=> nil
(0..(vowels_arr.length)) means you are passing to the block 0,1,2,3,4,5, and an attempt to access 5 gives nil, as in your array in 5th index is nil. See why the code (0...(vowels_arr.length)).all? {|i| vowels_arr[i] <= vowels_arr[i + 1]} failed by the below debugging with each to see what has been passed to the block:
vowels_arr = ["a","e","i","o","u"]
(0...(vowels_arr.length)).each {|i| p vowels_arr[i],"--",vowels_arr[i+1]}
p (1...3).to_a
Output:
"a"
"--"
"e"
"e"
"--"
"i"
"i"
"--"
"o"
"o"
"--"
"u"
"u"
"--"
nil

Resources