I'm learning Ruby and have seen (and using) regex matching in this way:
a = "string9"
if a =~ /\d/
#do something
end
The code would work but today I read the documentation on Regex and learned that =~ returns the position of the match in the string, nil if no match. I thought =~ returns true or false when it is the === that returns true for match, and false for no match. It seems the if statement in the above code should be rewritten:
if /\d/ === a
I have tried both and the program would run without error. I'm just trying to understand what's going on. It seems "if" will take anything but "nil" as true. I guess my question is less about regex but more about how the if statement (and other boolean statements) works.
The predicate after if will be treated as a boolean value.
In Ruby, only false and nil are treated as false. Anything other will be evaluated to true, so 0, [], {} are all true in boolean context.
From Ruby doc:
nil and false are both false values. nil is sometimes used to indicate "no value" or "unknown" but evaluates to false in conditional expressions.
true is a true value. All objects except nil and false evaluate to a true value in conditional expressions.
And you may have a look at the control expressions.
Related
I'm testing some strings to make sure they start with a letter:
name =~ /\A[a-zA-Z].*/
but since in Ruby this evaluates to either nil or 0 and both cast to false, I need to put an additional .nil? test:
if(name =~ /\A[a-zA-Z].*/).nil? ...
Is this the proper way or am I missing something?
EDIT:
Thanks for the replies, in my ignorance I made wrong assumptions, oversimplified the example. It should read (note the negation):
name !=~ /\A[a-zA-Z].*/
irb(main):001:0> a = "abc"
=> "abc"
irb(main):006:0> (a !=~/\Aabc/)
=> true
irb(main):007:0> (a !=~/\Ab/)
=> true
but since in Ruby this evaluates to either nil or 0 and both cast to false
wrong, only nil (and false, to be precise) are treated as false in conditionals. 0 is treated as true. So
if name =~ /\A[a-zA-Z].*/
is perfectly ok.
About your edited question, you're not allowed to add exclamation sign (!) to any operator to make it negated operator. There's no such operator (BTW, these 'operators' are actually methods) as !=~, so to achieve your goal. you should do:
if !(name =~ /\A[a-zA-Z].*/)
or you can use unless instead:
unless name =~ /\A[a-zA-Z].*/
I'm a ruby newbie and I'm having trouble understanding why "S" == /[S]/ evaluates to false.
Can anyone explain this? I've also tried
"S" =~ /[S]/
#=> 0 `
"S" =~ /[^S]/
#=> nil
baffling to me
"S" == /[S]/ is false because == in Ruby doesn't evaluate whether a regexp matches, it just determines whether two objects are equal. The string "S" and the regexp /[S]/ are of course completely different objects and not equal.
=~ (which is a correct way to match a regexp against a string in Ruby) returns the match position. In your first example the match position is the beginning of the string, 0. In the second example there is no match, so =~ returns nil.
"S" == /[S]/
Everything (almost) in Ruby is an object. In this case you are checking for equality between an instance of a String "S" and an instance of a Regexp /[S]/. Therefore, by definition, they are two different objects, hence the expression returns false. Rather than checking for equality with == you should use =~
"S" == /[S]/
When you use a match operator =~ it returns the index of the first match found in a string. Remember that indexing in Ruby starts from 0. In your example the first character in the provided string is matched. The first character is indexed with 0 and that is what the statement returns.
"S" == /[^S]/
By using a caret ^ you are telling Ruby to match anything but what is between square brackets (this is only true in square brackets, ^ is also used to indicate the beginning of a string if used outside []). In your case it is anything but S. Ruby does not find a match and returns nil.
I got a variable string which equals shid!
I'd like a regular expression that renders true if the inputed string matches every character of the previous variable string in the good order.
As I always fail to explain properly what I want to do, here is an example of possible inputs and outputs:
shid! returns true
shid returns false
shhhhiddd! returns true
ssssshhhhiiiiiddd!!!! returns true
hsid! returns false
For the ritual question "What did I try", I'm not used to regular expression. So my answer would be this: "shid!" =~ /s.*!/, and obviouly this isn't the good answer and I know why, but don't know how to fix it. Thanks for the kind help
Try:
string = 'shid!'
reg = Regexp.new string.split('').join('+')
!!('shid!' =~ reg) #=> true
!!('shid' =~ reg) #=> false
!!('shhhhiddd!' =~ reg) #=> true
!!('ssssshhhhiiiiiddd!!!!' =~ reg) #=> true
!!('hsid!' =~ reg) #=> false
You can do using #squeeze
str.squeeze == 'shid!' #=> true
I'm not sure I've got the question. But maybe it's what you are looking for:
str = 'ssssshhhhiiiiiddd!!!!'
str.split('').uniq.join == 'shid!' #=> true
This code:
/ell/ === 'Hello'
evalutes to 'true' in IRB.
I don't understand why this makes sense logically. Integer === 30 makes sense because 30 is a PART OF the Integer class, but in what way is the string 'Hello' a PART OF /ell/? I don't get it.
Semantically you're saying does the regular expression 'ell' match the string 'Hello'. Since 'Hello' contains the substring 'ell', it is true.
The '===' method is described here:
http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-i-3D-3D-3D
You should not use === for anything in ruby except case equality, find the documentation on Regex#===
Following a regular expression literal with the === operator allows you to compare against a String.
/^[a-z]$/ === "HELLO" #=> false
/^[A-Z]$/ === "HELLO" #=> true
The === the case operator, it is primarily used in case statements and should not really be seen by its own.
case my_string
when /ll/ then puts 'the string migth be hello'
when /x/ then puts 'all i know is that the sting contain x'
else puts 'I have no idea'
end
It can also be used in some other functions such as grep:
array = ['ll', 'aa', 'hello']
p array.grep(/ll/){|x| x.upcase} #=> ["LL", "HELLO"]
Any other use is discouraged and it really does not need to make any sense.
A regular expression describes a language, i.e. a set of strings. The === checks whether the string is a member of that set.
See my answer to a similar question for details.
After reading a comment to an answer in another question and doing a little research, I see that =~ is defined on Object and then overridden by String and Regexp. The implementations for String and Regexp appear to assume the other class:
"123" =~ "123" # => TypeError: type mismatch: String given
/123/ =~ /123/ # => TypeError: can't convert Regexp to String
Although =~ is defined for Object, + is not:
Object.new =~ 1 # => nil
Object.new + 1 # => undefined method `+' for #<Object:0x556d38>
Why has Object#=~ been defined, rather than restricting =~ to to String and Regexp?
Because it allows any object to be used in a match expression:
Object.new =~ /abc/
=> nil
I guess this makes sense in the way that Object.new does not match the regexp /abc/ and the code would blow up if the left argument wasn't a String object. So it generally simplifies the code because you can have any object on the left side of the =~ operator.
Well, I suppose that's actually nicely answered in String =~ documentation:
Match — If obj is a Regexp, use it as a pattern to match against str,and
returns the position the match starts, or nil if there is no match.
Otherwise, invokes obj.=~, passing str as an argument. The default =~
in Object returns nil.
The point is, you can write your own implementation of Object =~ - and it will be used in String =~ Not Regexp statement.
From your comments, your actual question is why is =~ defined on Object while + isn't.
The reason is that Object#=~ can return nil for random objects (since they don't match), but Object#+ can not return a meaningful result.
It is not necessarily super useful, but it can not be said to be false (you would have to show a match to prove that a nil result is a contradiction). See the mathematical concept of vacuous truth. On the other hand, any result for Object.new + 1 could lead to contradictions.
This is similar to <=> that can return nil (and is thus also defined on Object) while <, >, ..., can not return true nor false while being completely consistent. Note that for Class#> it was decided to return nil in those cases.