ruby's =~ operator return values? - ruby

def starts_with_consonant?(s)
if /^(a|e|i|o|u).*/i =~ s
true
else
false
end
end
# prints out true
puts starts_with_consonant?('aa')
# prints out false
puts starts_with_consonant?('da')
If I change the code just to
def starts_with_consonant?(s)
/^(a|e|i|o|u).*/i =~ s
end
Is that same functionality because
puts starts_with_consonant?('aa').inspect
prints out 0 (Shouldn't it be 1?)
puts starts_with_consonant?('da').inspect
prints out nil
# both print out 0
puts starts_with_consonant?('aa').to_i
puts starts_with_consonant?('da').to_i
What gives?

The =~ operator returns the first match index if the String and Regexp match, otherwise nil is returned:
'foo' =~ /bar/ # => nil
'foo bar' =~ /bar/ # => 4

Your first method, with the if/else statement, is treating the result of the =~ check as "truthy value or not?". If the match is found in the string, it returns the index (in your case, 0) or if it is not found, it returns nil.
0 is a truthy value; nil is not.
Therefore, even though it's returning the same result in each of your methods containing the /.../ =~ s expression, you get different return values out of the methods, depending on what you do with that result.
In the if/else statement, you get true when it's the truthy value of 0, and false when it's the non-truthy value of nil.
In the bare return statement, you get the plain return values of 0 and nil.

well the #=~ method actually returns the index where the first match occurs.
You can't do nil.to_i because that produces zero.
[6] pry(main)> nil.to_i
=> 0

puts starts_with_consonant?('aa').inspect
prints out 0 (Shouldn't it be 1?)
No, it should be 0. Strings are zero-indexed, the pattern has been found on the zeroth position. 0 is a truthy value, triggering the if clause if evaluated there. 'da' =~ /a/ would return 1, since a is the 1st character in the string (d being 0th).
puts starts_with_consonant?('da').inspect
prints out nil
There is no position that matches the pattern, so the return value is nil, a falsy value, which triggers the else clause if evaluated as an if condition.
# both print out 0
puts starts_with_consonant?('aa').to_i
puts starts_with_consonant?('da').to_i
Because both 0.to_i and nil.to_i result in 0.

Your getting back truthy. You can't print it but you can use it, e.g.
2.0.0-p247 :007 > if "aaaabcd" =~ /a/ then puts "true" end
true
=> nil
2.0.0-p247 :008 > if "aaaabcd" =~ /aaa/ then puts "true" end
true
=> nil
2.0.0-p247 :009 > if "aaaabcd" =~ /z/ then puts "true" end
=> nil
Similarly you can set a variable based on the evaluation, i.e.
2.0.0-p247 :013 > if "aaaabcd" =~ /a/ then b=1 end
=> 1
2.0.0-p247 :014 > if "aaaabcd" =~ /aaa/ then b=1 end
=> 1
2.0.0-p247 :015 > if "aaaabcd" =~ /zzz/ then b=1 end
=> nil

Related

ruby or operator behaving differently inside initialise method

fairly new to programming and came across the following code which does gives the intended result, that is it stores the object in the instance variable if passed as an argumnet or defaults to generating a new object and storing that. but the syntax is using an or(||) operator which as i understood always returns a boolean. so is it behaving differently or there is more to or/and operators?
def initialize(args = {})`
#sms = args[:sms] || Messenger.new
end
The or operator in ruby does not "return" a boolean: it evalutes to the first expression if the first expression is truthy (a term which means everything but false and nil), otherwise it evalutes to the latter
5 || nil => 5
nil || 5 => 5
false || nil => nil
nil || false => false
in ruby, everything can be evaluated as an expression, and an expression will return a value
so || and even && do not return a boolean it will return a truthy or falsy value
in the case of || it will check if the first expression is truthy if not it will go to the next one, then the next one, if no truthy value is found it will return the last one which will be falsy as well
irb(main):004:0> nil || "true value" || 0
=> "true value"
irb(main):006:0> nil || false || "true value" || 0 || true
=> "true value"
in the case of && it will do the opposite, it will return the first falsy value it can find
irb(main):001:0> nil && false && "true value" && 0 && true
=> nil
and the if condition will understand truthy and falsy values which will be what these logical operators return
rb(main):007:0> if "true"
irb(main):008:1> puts "true"
irb(main):009:1> else
irb(main):010:1> puts "false"
irb(main):011:1> end
true
irb(main):017:0> if nil
irb(main):018:1> puts "true"
irb(main):019:1> else
irb(main):020:1> puts "false"
irb(main):021:1> end
false

Take in string, return true if after "a", a "z" appears within three places

# Write a method that takes a string in and returns true if the letter
# "z" appears within three letters **after** an "a". You may assume
# that the string contains only lowercase letters.
I came up with this, which seems logical, but for some reason if "z" comes directly after "a", it returns false. Can someone explain why?
def nearby_az(string)
i = 0
if string[i] == "a" && string[i+1] == "z"
return true
elsif string[i] == "a" && string[i+2] == "z"
return true
elsif string[i] == "a" && string[i+3] == "z"
return true
else return false
end
i += 1
end
#shivram has given the reason for your problem. Here are a couple of ways to do it.
Problem is tailor-made for a regular expression
r = /
a # match "a"
.{,2} # match any n characters where 0 <= n <= 2
z # match "z"
/x # extended/free-spacing regex definition mode
!!("wwwaeezdddddd" =~ r) #=> true
!!("wwwaeeezdddddd" =~ r) #=> false
You would normally see this regular expression written
/a.{0,2}z/
but extended mode allows you to document each of its elements. That's not important here but is useful when the regex is complex.
The Ruby trick !!
!! is used to convert truthy values (all but false and nil) to true and falsy values (false or nil) to false:
!!("wwwaeezdddddd" =~ r)
#=> !(!("wwwaeezdddddd" =~ r))
#=> !(!3)
#=> !false
#=> true
!!("wwwaeezdddddd" =~ r)
#=> !(!("wwwaeeezdddddd" =~ r))
#=> !(!nil)
#=> !true
#=> false
but !! is not really necessary, since
puts "hi" if 3 #=> "hi"
puts "hi" if nil #=>
Some don't like !!, arguing that
<condition> ? true : false
is more clear.
A non-regex solution
def z_within_4_of_a?(str)
(str.size-3).times.find { |i| str[i]=="a" && str[i+1,3].include?("z") } ? true : false
end
z_within_4_of_a?("wwwaeezdddddd")
#=> true
z_within_4_of_a?("wwwaeeezdddddd")
#=> false
This uses the methods Fixnum#times, Enumerable#find and String#include? (and String#size of course).
Your solution is incorrect. You are considering only the case where String starts with a (with i = 0 at the start of your method). I can see you are incrementing i at the end, but its of no use as its not in a loop.
I can think of a solution as to find the index of a in string, then take substring from that index + 3 and look for z. Something like:
s = "wwwaeezdddddd"
s[s.index("a")..s.index("a")+3]
#=> "aeez"
s[s.index("a")..s.index("a")+3] =~ /z/ # checking if z is present
#=> 3
If a can occur more than once in input String, you need to find all indices of a and run the above logic in a loop. Something like:
s = "wwwaesezddddddaz"
indexes = (0 ... s.length).find_all { |i| s[i,1] == 'a' }
#=> [3, 14]
indexes.each { |i| break if #is_present = s[i..i+3] =~ /z/ }
#is_present
#=> 1
Let’s implement the FSM ourselves :)
input = "wwwaeezdddddd"
!(0...input.length).each do |idx|
next unless input[idx] == 'a' # skip unrelated symbols
current = (idx..[idx + 3, input.length - 1].min).any? do |i|
input[i] == 'z' # return true if there is 'z'
end
# since `each` returns truthy (range itself),
# in case of success we return falsey and negate
break false if current
end
#⇒ true
Please note, that the above implementation is O(length(input)) and does not use any built-in ruby helpers, it is just iterating a string char by char.
While the regexp solution is the most elegant, here is one for completion, which is more in spirit to your original attempt:
def nearby_az(string)
!!(apos = string.index('a') and string[apos,3].index('z'))
end

Function to strip!, then convert zero-length string to nil

What's the most-efficient manner to remove beginning and ending spaces around a string, then convert the string to nil if the resulting value is zero-length?
For example:
> a=''
> squash(a)
=> nil
> a=' '
> squash(a)
=> nil
> a=' xyz '
> squash(a)
=> 'xyz'
> a=nil
> squash(a)
=> nil
Thus far:
def squash(value)
return nil if value.nil?
value.strip!
(value.blank? ? nil : value)
end
Seems like there could be a more-terse way of implementing this.
** edit **
While I am working in Rails, it would be nice if the answer would contain a Ruby-only implementation, too.
I should emphasize that the implementation needs to be able to handle a string with a nil value.
Assuming you want this for rails (otherwise blank? is undefined) you can use presence method:
def squash(value)
value && value.strip.presence
end
In pure ruby, I would do:
def squash(value)
return unless value
value = value.strip
value unless value.empty?
end
This will work with plain Ruby:
def squash(str)
str = str.to_s.strip
str unless str.empty?
end
Here's one way:
def squash(str)
(str && str[/\S/]) ? str.strip : nil
end
/\S/ looks for a character that is not whitespace.
squash " My dog has fleas. " #=> "My dog has fleas."
squash " " #=> nil
squash nil #=> nil
Reader challenge
I tried to also implement squash!, that would convert the argument str in place. If str is nil, just leave it alone. If str contains a least one non-whitespace character, then str.strip!. However, I could not figure out a way to convert a string to nil. I wanted to do this when the string is empty or contains only whitespace, but the problem is to convert any string, or more generally, any non-nil object, to nil, when the object is received as a method argument. Can it be done? [Edit: #Stefan says the type cannot be changed. I'm sure he's right, but I would like to see where that is written and understand why it is not permitted. Anyone? tidE].
This handles all your examples.
def squash(value)
value.to_s.strip.empty? ? nil : value.strip
end
Just adding this because it's short:
def squash(str)
str.to_s[/\S(.*\S)?/]
end
squash(nil) #=> nil
squash("") #=> nil
squash(" ") #=> nil
squash("a") #=> "a"
squash(" a") #=> "a"
squash("a ") #=> "a"
squash(" a ") #=> "a"
squash(" foo ") #=> "foo"
squash(" foo bar ") #=> "foo bar"
Here's a plain ruby version:
def squash(str)
str && str.strip! && (str unless str.empty?)
end
Update - If you want a version without side effects:
def squash(str)
str && (x = str.strip) && (x unless x.empty?)
end
If you want the method name squash with argument value.
def squash(value)
return value unless value.instance_of?(String)
return if value.strip!&.empty?
value
end
Features:
Works either on pure Ruby or Ruby on Rails
Works with other data types than string as well, for example, you can pass a number if you want
Testing:
squash('ok')
#=> ok
squash('')
#=> nil
squash(' ')
#=> nil
squash(' xyz ')
#=> 'xyz'
squash('xyz ')
#=> 'xyz'
squash(' xyz')
#=> 'xyz'
squash(123)
#=> 123
squash(nil)
#=> nil
Note:
I use safe navigation operator, which was released in Ruby 2.3.0. So make sure before using it.
irb(main):001:0> s = " string "
=> " string "
irb(main):002:0> s.strip!
=> "string"
irb(main):003:0> s.blank?
NoMethodError: undefined method `blank?' for "string":String
from (irb):3
from C:/RUBY/BIN/irb:12:in `'
irb(main):004:0>
I think blank is not Ruby but Rails? Anyway, what's wrong with
(value.length == 0 ? nil : value)
or even better
value.empty? ? nil : value
At least everybody would understand what the intention is here.

How "=~" operator works inside the "IF" block in Ruby?

Hi find the below two blocks:
Block-I
irb(main):001:0> s="acbbdd"
=> "acbbdd"
irb(main):002:0> /e/=~s
=> nil
irb(main):003:0> if /e/=~s then
irb(main):004:1* print "h"
irb(main):005:1> end
=> nil
Block-II
irb(main):001:0> s="acbbdd"
=> "acbbdd"
irb(main):006:0> if /c/=~s then
irb(main):007:1* print "h"
irb(main):008:1> end
h=> nil
irb(main):009:0>
Could you please help me to understand how =~ works in I and II block? In the first block it doesn't match and returns nil but in the second block how nil is coming?
In the first block, the string doesn't match /e/ - there's no e in "acbbdd".
In the second block, the string does match /c/ - there is a c in "acbbdd".
The reason the nil appears is because it is the return value of print, and hence of the entire if block.
In II , /c/ matches s, so the print "h" get executed. You get an output (the string "h") and a return value from the print statement (nil)
Then the if block returns the return value of the last statement in the block, in this case nil.
run print "h" along will give you same result.

Binary or "|" in ruby

Why isnt that working:
>> s = "hi"
=> "hi"
>> s == ("hi"|"ho")
NoMethodError: undefined method `|' for "hi":String
from (irb):2
>>
I don't get it.. Is there a solution for this kind of syntax? Because
s == ("hi"|"ho")
#is shorther than
s == "hi" || s == "ho"
Yes, the bitwise operator | is not defined in the String class: http://ruby-doc.org/core/classes/String.html
Consider this for expressiveness:
["hi", "ho"].include? myStr
irb(main):001:0> s = "hi"
=> "hi"
irb(main):002:0> ["hi", "ho"]
=> ["hi", "ho"]
irb(main):003:0> ["hi", "ho"].include? s
=> true
irb(main):004:0> s = "foo"
=> "foo"
irb(main):005:0> ["hi", "ho"].include? s
=> false
In most high level languages that syntax will not work, you have to stick to the longer syntax of:
s == "hi" || s == "ho"
Note that | is a bitwise or, whereas || is a regular or
You could use the include? method on array if you've got several == tests to do:
["hi", "ho"].include?(s)
Not shorter for two checks admittedly but it will be shorter for three or more.
This syntax doesn't exist in any language as far as I know.
What you are saying
s == ("hi"|"ho")
Literally translates to 'bitwise OR the strings "hi" and "ho" together and then compare them with s'. If you can't see why this is not what you are looking for, try writing down the ASCII codes for "hi" and "ho" and then bitwise ORing them together. You are going to get complete gibberish.
You could make it work that way:
irb> class Pair
def initialize(strA,strB)
#strA,#strB = strA,strB
end
def ==(string)
string == #strA || string == #strB
end
def |(other)
Pair.new(self,other)
end
end
#=> nil
irb> class String
def |(other)
Pair.new(self,other)
end
alias old_equals :==
def ==(other)
if other.kind_of? Pair
other == self
else
old_equals other
end
end
end
#=> nil
irb> ("one"|"two") == "one"
#=> true
irb> ("one"|"two") == "two"
#=> true
irb> ("one"|"two") == "three"
#=> false
irb> "one" == ("one"|"two")
#=> true
irb> "three" == ("one"|"two"|"three")
#=> true
But since this involves some monkey-patching of a fairly lowlevel class, I wouldn't advise relying on it. Other people will hate reading your code.
Ruby supports binary 'or' and other binary operations on values of type Fixnum and Bignum, meaning any integer. Bitwise operations aren't supported on strings or any other type, as far as I know.
As other people have mentioned, you probably want something other than binary operations altogether. However, you can easily get integer representations of characters, so you can compare characters like so:
a = "Cake"
b = "Pie"
puts a[0] | b[0] # Prints "83" - C is 67 and P is 80.
You can get an array of the comparisons easily with some conversions.
a = "Cake"
b = "Pie " # Strings of uneven length is trivial but more cluttered.
a_arr = a.split(//)
b_arr = b.split(//)
c_arr = []
a.each_with_index { |char, i| c.push(a[i].to_i | b[i].to_i) }
# If you *really* want an ASCII string back...
c = c_arr.collect(&:chr).join
You could use a regex:
Like so:
regex = /hi|ho/
s = "hi"
t = "foo"
s =~ regex
#=> 0
t =~ regex
#=> nil

Resources