Related
I have a hard time understanding the following code segment from the Ruby docs:
a = "hello world"
a.count "lo" #=> 5
a.count "lo", "o" #=> 2
a.count "hello", "^l" #=> 4
a.count "ej-m" #=> 4
"hello^world".count "\\^aeiou" #=> 4
"hello-world".count "a\\-eo" #=> 4
especially this code a.count "ej-m". Can anyone please explain how it works?
Just imagine the "pattern" strings as wrapped by [ and ] from regex syntax, that are matched against each character.
So, if we break a = "hello world" into characters:
[1] pry(main)> a = "hello world"
=> "hello world"
[2] pry(main)> a.split('')
=> ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]
And convert "ej-m" to regex wrapped with [ and ] we get /[ej-m]/ - which means either 'e' or any character from 'j' to 'm'(including both):
[3] pry(main)> a.split('').select{|c| c=~ /[ej-m]/}
=> ["e", "l", "l", "l"]
We got 4 matches - which is also the result you get. Essensially a.count "ej-m" is equivalent to:
[4] pry(main)> a.split('').count{|c| c=~ /[ej-m]/}
=> 4
Multiple arguments to the method are just and between the matches:
[5] pry(main)> a.split('').count{|c| c =~ /[hello]/ and c =~ /[^l]/}
=> 4
The sequence c1-c2 means all characters between c1 and c2.
So you are providing a range, basically it counts which characters are in that range (>= c1 && <= c2)
i.e:
a = "hello world"
a.count "a-z"
=> 10
a.count "o-w"
=> 4 #(o, o, r, w)
a.count "e-l"
=> 5 #(h, e, l, l, l)
We find that
"hello world".count("ej-m") #=> 4 (_ell_____l_)
Examine the doc for String#count carefully.
Here is how count might be implemented to deal with patterns that closely resemble the pattern "ej-m".
def count_letters(str, pattern)
idx = pattern[1..-2].index('-')
if idx
idx += 1
before, after = pattern[idx-1], pattern[idx+1]
pattern[idx-1..idx+1] = (before..after).to_a.join
end
str.each_char.sum { |c| pattern.include?(c) ? 1 : 0 }
end
count_letters(str, pattern) #=> 4 (_ell_____l_)
However, String#count must also do the following.
Allow for multiple ranges in the pattern
"hello1world".count("e0-9j-mv-x") #=> 6 (_ell__1_w__l_)
If the pattern begins with the character '^'count the number of characters that do not match the remainder of the pattern
"hello world".count("^ej-m") #=> 7 (h___o*wor_d) * = space to count
"hello^world".count("e^j-m") #=> 5 (_ell_^___l_)
"hello world".count("\^ej-m") #=> 7 (h___o*wor_d) * = space to count
Note that escaping '^' at the beginning of the string makes no difference.
Match a hyphen
"hello-world".count("ej-m-") #=> 5 (_ell_-___l_)
"hello-world".count("-ej-m") #=> 5 (_ell_-___l_)
"hello-world".count("ej\-m") #=> 4 (_ell____l_)
Note that escaping a hyphen that is not the first or last character of the pattern makes no difference.
Match a backslash
'hello\world'.count("ej-m\\") #=> 5 (_ell_\___l_)
'hello\world'.count("\\ej-m") #=> 4 (_ell____l_)
Note that a backslash at the beginning of a string is disregarded.
Some of the above results (Ruby v2.4) do not seem to be consistent with the documentation.
I'm still coming to terms with Regex and want to formulate an expression that will let me count the number of successive consonants at the beginning of a string. E.g. 'Cherry' will return 2, 'hello' 1, 'schlepp' 4 and so on. Since the number isn't predetermined (although English probably has some upper limit on initial consonants!) I'd need some flexible expression, but I'm a bit stuck about how to write it. Any pointers would be welcome!
This would work:
'Cherry'[/\A[bcdfghjklmnpqrstvwxyz]*/i].length #=> 2
The regex matches zero or more consonants at the beginning of the string. String#[] returns the matching part and length determines its length.
You can also express the consonants character class more succinct by intersecting [a-z] and [^aeiou] via &&:
'Cherry'[/\A[a-z&&[^aeiou]]*/i].length #=> 2
Something along this line would work:
>> 'Cherry'.downcase.split(/([aeiou].*)/).first.length
# => 2
>> 'hello'.downcase.split(/([aeiou].*)/).first.length
# => 1
>> 'schlepp'.downcase.split(/([aeiou].*)/).first.length
# => 4
Another way is to replace from the first vowel until end of string by nothing then take the length:
'Cherry'.gsub(/[aeiou].*$/,"").length
It is not necessary to use a regular expression.
CONSONANTS = (('a'..'z').to_a - 'aeiou'.chars).join
#=> "bcdfghjklmnpqrstvwxyz"
def consecutive_constants(str)
e, a = str.each_char.chunk { |c| CONSONANTS.include?(c.downcase) }.first
e ? a.size : 0
end
consecutive_constants("THIS is nuts") #=> 2
consecutive_constants("Is this ever cool?") #=> 0
consecutive_constants("1. this is wrong") #=> 0
Note
enum = "THIS is nuts".each_char.chunk { |c| CONSONANTS.include?(c.downcase) }
#=> #<Enumerator: #<Enumerator::Generator:0x000000010e1a40>:each>
We can see the elements that will be generated by this enumerator by applying Enumerable#entries (or Enumerable#to_a):
enum.entries
#=> [[true, ["T", "H"]], [false, ["I"]], [true, ["S"]], [false, [" ", "i"]],
# [true, ["s"]], [false, [" "]], [true, ["n"]], [false, ["u"]], [true, ["t", "s"]]]
Continuing,
e, a = enum.first
#=> [true, ["T", "H"]]
e #=> true
a #=> ["T", "H"]
a.size
#=> 2
How to check if a string contains only paranthesis, comma, number, and any combination of "urld" separated with a string? It should always be in the below form:
(0,3),u,r,u,l,u #True
(0,3),u,r,u,l,u #True
(0,3),u,r,u,l u #False because of space b/w l and u
I tried something like this but seems like I am off:
if !misc_text.match(/^(\d+,\d+\)(,[udlr])+)/)
The examples suggest that not all of the letters 'u', 'r', 'l', 'd' need be present. I have assumed that the string may contain any number of each of these four letters (in addition to the other permitted characters). I have also assumed that 'any combination of "urld" separated with a string' means that each pair of these four characters must be separated by one or more of the other permitted characters. This is one way to accomplish that:
def check_it(s)
(s.chars.chunk {|c| c =~ /[urld]/}.to_a.size == s.count('urld') &&
s =~ /^[(),\durld]*$/) ? true : false
end
check_it('(0,3),u,r,u,l,u') #=> true
check_it('(0,3),u,r,u,l u') #=> false
check_it('(0,3),u,r,u,lu') #=> false
Suppose
s = '(0,3),u,r,u,lu'
Then
a = s.chars.chunk {|c| c =~ /[urld]/}.to_a
#=> [[0, ["u"]], [0, ["r"]], [0, ["u"]], [0, ["l", "u"]]]
a.size #=> 4
s.count('urld') #=> 5
As a.size < s.count('urld'), count_it() returns false
If instead:
s = '(0,3),u,r,u,l u'
then
s.chars.chunk {|c| c =~ /[urld]/}.to_a.size
#=> a = [[0, ["u"]], [0, ["r"]], [0, ["u"]], [0, ["l"]], [0, ["u"]]]
# a.size => 5
# 5 == s.count('urld') => true
but
s =~ /^[(),\durld]*$/ #=> nil
so check_it() => false.
Perhaps you need this regex: /^(\(\d+,\d+\)(,[udlr])+)$/.
You are able to do as follows, mixing the regexp, and array operations:
class String
def catch str
str.split('').all? {|l| /,#{l}(,|\b)/ =~ self }
end
end
so:
"(0,3),u,r,u,l,d".catch('ulrd') # => true
"(0,3),u,r,u,l d".catch('ulrd') # => false
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(:+).
Testing out some code in both pry and irb, I get the following results:
[1] pry(main)> a = [1, 3, 5, 7, 0]
=> [1, 3, 5, 7, 0]
[2] pry(main)> a.any? {|obj| p obj; 3 / obj > 1}
1
=> true
[3] pry(main)> a.all? {|obj| p obj; 3 / obj > 1}
1
3
=> false
In [2] and [3] I see that there appears to be short-circuit evaluation that aborts the iteration as soon as possible, but is this guaranteed behaviour? Reading the documentation there is no mention of this behaviour. I realise that I can use inject instead as that will iterate over everything, but I'm interested in finding out what the official Ruby view is.
Yes.
In the final draft of the Ruby standard, all? is defined as such:
Invoke the method each on the receiver
For each element X which the method each yeilds:
If block is given, call block with X as argument. If this call returns a falseish object, return false.
If block is not given, and X is a falseish object, return false.
Return true.
Note the word return in step 2. This guarantees short circuit evaluation. any? is defined similarly. However the standard is still a draft and I don't know which Ruby implementations (if any) aim to be standards-compliant.
The any? method just realizes the 'or' logic function over the Enumerable. It could be interpreted as statement:
y = x1 v x2 v x3 v ... v xn
And the all? method realizes 'and' logic function over the Enumerable. It also could be interpreted as statement:
y = x1 * x2 * x3 * ... * xn
Since the Array is an Enumerable, it also includes those methods. So, for the method any? the first occurience of true (exactly neither nil nor false) result breaks enumeration with true result. In example the yielded becomes true on number 4, so the methods breaks the execution the returns true:
[1,2,3,4,5].any? {| x | puts x ; x > 3 }
# 1
# 2
# 3
# 4
# => true
Also you can apply DeMorgan's rule to the function any?, and to use all? method:
![1,2,3,4,5].all? {| x | puts x ; x <= 3 }
# 1
# 2
# 3
# 4
# => true
For the method all? the first occurience of either false or nil result do something similar, i.e. returns false. In example the yielded becomes false on number 3, so the methods breaks the execution the returns false:
[1,2,3,4,5].all? {| x | puts x ; x < 3 }
# 1
# 2
# 3
# => false
And with DeMorgan's transformation to use any? method:
![1,2,3,4,5].any? {| x | puts x ; x >= 3 }
# 1
# 2
# 3
# => false
I think there is some ambiguity here.
Consider the following:
RUBY_VERSION
=> "2.3.7"
When yielding to a block:
[1,2,3,4,5].all? {| x | puts x ; x < 3 }
# 1
# 2
# 3
# => false
When not yielding to a block:
def a
puts "in a"
true
end
def b
puts "in b"
false
end
def c
puts "in c"
true
end
[a,b,c].all?
# in a
# in b
# in c
# => false
Seems like condition #2:
If block is not given, and X is a falseish object, return false.
Is not valid.