I don't understand the result of the first line. It's supposed to return a file name without extension if the file has one. Can somebody explain to me why it is like that and also tell me what would be a more proper here?
irb(main):003:0> 'fafeafeafewafeawfeaw'.sub!(/\.[^\.]*$/, '')
=> nil
irb(main):004:0> '.fafeafeafewafeawfeaw'.sub!(/\.[^\.]*$/, '')
=> ""
irb(main):005:0> 'fafeafeafewafea.wfeaw'.sub!(/\.[^\.]*$/, '')
=> "fafeafeafewafea"
It is documented that the sub! (like many of the ! string operations) return nil if no change was made.
From the docs
Performs the substitutions of String#sub in place, returning str, or nil if no substitutions were performed.
Instead use the regular sub. In your case the extra bang (!) is unnecessary.
'fafeafeafewafeawfeaw'.sub(/\.[^\.]*$/, '')
Bang Methods
The difference between sub and sub! is subtle. But in ruby in general, the non bang (!) version of a method is safer. Since by convention the bang means the method has more side affects.
In the case of string functions (and many array/enumerable functions) the bang means the method operates on the contents of the caller, instead of making (and returning) a copy.
s = 'fafafa'
puts s #=> 'fafafa'
puts s.sub(/fa/, 'fo') #=> 'fofofo'
puts s #=> 'fafafa'
puts s.sub!(/fa/, 'fo') #=> 'fofofo'
puts s #=> 'fofofo'
Related
Codeacademy teaches that you can chain multiple methods together as such:
user_input.method1.method2.method3
However, in a later lesson they display some methods like this:
user_input = gets.chomp
user_input.downcase!
I combined them:
user_input = gets.chomp.downcase!
When I use it this way:
user_input = gets.chomp.downcase!
if user_input.include? "s"
...
I receive an error "undefined method `include?'". If I change it to the following, it works fine:
user_input = gets.chomp
user_input.downcase!
if user_input.include? "s"
...
I'm at a loss. I'm concerned whether or not this is a quirk with their console or if this is just how I should be doing it in Ruby. If someone could tell me which way is right, I'd appreciate it. If both are right, that's OK too.
Firstly, in case you do not yet fully understand, assignment of values to variables are done through =, and that you could inspect what variable type it is by appending .class to anything.
Consider the following:
name = 'John'
puts name
# => John
puts name.class
# => String
Now, secondly, it should be noted that the return values of ALL methods are ALL different. But all of them can be identified into two types:
Methods that:
return self
return anything other than self
Example for 1.
-- methods that return self, which you could say methods that return the same type of object which in our specific case, a String
name = 'John'
puts name
# => 'John'
puts name.class
# => String
downcased_name = name.downcase
puts downcased_name
# => john
puts downcased_name.class
# => String
deleted_downcased_name = downcased_name.delete('h')
puts deleted_downcased_name
# => jon
puts deleted_downcased_name.class
# => String
# All of the above can be just simplified into:
deleted_downcased_name2 = 'John'.downcase.delete('h')
puts deleted_downcased_name2
# => jon
puts deleted_downcased_name2.class
# => String
Notice that deleted_downcased_name and deleted_downcased_name2 are the same, because you could treat the chained methods as if you are chaining the return values which is 'John' -> 'john' -> 'jon'.
Example for 2
-- methods that return anything but self, which you could say methods that return a different type.
In our specific case, String's downcase! returns either a String or NilClass (reference here)
returning String if the string changes, or
returning nil if string is already downcased to begin with (no change).
or another String's method: start_with? (reference here)
returning true or false
This is where chaining of methods will not work (raises an error), when you try to use a String method as a chain to nil value.
Consider the following
name = 'mary'
puts name
# => 'mary'
puts name.class
# => String
downcased_name = name.downcase!
puts downcased_name
# => nil
puts downcased_name.class
# => NilClass
downcased_name.delete('h')
# => This will raise the following error
# NoMethodError: undefined method `delete' for nil:NilClass
The error above is because downcased_name is a type of NilClass where you are expecting it to be a type of String. Therefore you cannot chain any string method on it anymore. You can only chain String methods on a String type of value. Similarly, you can only chain Number methods on a Number type of value.
Whenever in doubt, you could always check the documentation to check what a method does, and what its return value and type.
The problem you are encountering is with the bang method downcase!.
This is basically saying "mutate the original string so that it is downcase".
The important part is that this returns nil. As such you are actually calling include? on nil.
If you use the non bang method downcase instead, it is saying "downcase the previously chained thing but do not mutate the original". The key difference is that it returns the result rather than nil.
Here is an example:
str = "ABCD"
str.downcase!
=> nil
str
=> "abcd"
str = "ABCD"
str.downcase
=> "abcd"
str
=> "ABCD" # Note str is still unchanged unless you set str = str.downcase
Welcome to Ruby! While your apprenticeship at Codeacademy may be limited, you'll continue to refer to language API documentation throughout your career. API documentation is a description of what the language (or a library) does for you. In this case, you're using downcase! which, as one commenter points out, does not always return a String. When it takes no action, it returns nil. Nil is an Object in Ruby (like everything else), but the 'include?' method isn't defined for nil, which explains your error. (It's one of the most common errors in Ruby, learn its meaning.)
So, in fact, what's breaking here isn't your method chain. It's that one of the intermediate methods isn't returning a value of the type you expect (nil instead of some kind of String).
Chaining non destructive methods like:
string.chomp.downcase...
has the advantage that the code is concise, but is not efficient if you are not interested in the original state of the object, and just want the final result because it creates intermediate objects during the chain.
On the other hand, applying destructive methods sequentially to the same object:
string.chomp!
string.downcase!
...
is more efficient if you do not need to keep the original state of the object, but is not concise.
Combining methods that may return an object of a different class (particularly nil) as:
string = gets.chomp!.downcase!...
is wrong because the result can become nil at some point in the chain.
Applying a potentially nil-returning method at only the last position as you did:
string = gets.chomp.downcase!
is still not useful if you expect string to always be a string, and can easily lead to an error as you did.
If you want to chain these methods in you example, perhaps you can do this:
user_input = gets.tap(&:chomp!).tap(&:downcase!)
if user_input.include?("s")
...
Here are two tests:
if [1,2,3,4].include? 2 && nil.nil?
puts :hello
end
#=>
and
if [1,2,3,4].include?(2) && nil.nil?
puts :hello
end
#=> hello
The above tells me that && has higher precedence than method arguments so it logically ands 2 && nil.nil? which is true and passes that as an argument to include?.
However, there is this test:
if [1,2,3,4].include? 2 and nil.nil?
puts :hello
end
#=> hello
So this is telling me that method arguments and 'and' have the same precedence (or method args are higher than 'and') since it passed 2 to include? before it processed 'and'.
Note: I understand that && and and have different precedence. The question is not regarding this but regarding and or or vs the arguments to a ruby method.
I can't find documentation that affirms this. For instances, this doesn't mention method arguments at all: http://phrogz.net/programmingruby/language.html#table_18.4 or http://romhack.wikia.com/wiki/Ruby_operators.
Could anyone explain this behavior? Namely in that how does ruby know to pass values as arguments to a method vs. process operators?
As you said && and and have different precedence, however the explanation for the following example:
if [1,2,3,4].include? 2 and nil.nil?
puts :hello
end
#=> hello
is the binding strenght of the and as you can read here:
Difference between "and" and && in Ruby?
This basically explains that 2 and nil.nil? will be evaluated as nil, however it will return 2 as can be seen in this example:
foo = :foo
bar = nil
a = foo and bar
# => nil
a
# => :foo
a = foo && bar
# => nil
a
# => nil
I've never seen any documentation about method argument precedence, but one rule of thumb I use when seeing method arguments is to mentally strip the whitespace wherever possible in the arguments and still have the same expression. This normally gives me the precedence:
[1,2,3,4].include? 2&&nil.nil? is the same expression, but you cannot strip the whitespace in
[1,2,3,4].include? 2 and nil.nil? and therefore, the precedence is left to right ... I.e. Method argument is 2.
Anyway, the better question is why on earth would you write statements like this?
Omitting method parenthesis is only useful for code readability. However, your statements are hardly readable and makes one pause over the code and think about it more than he should. If I was to review code like this, I would definitely fail the code review due to poor readability.
In fact, many style guides explicitly state that most methods with arguments should be parenthesized (is this even a word ;). For example:
Ruby style guide
Ruby uses === operator on the case/when type execution style.Now It also known that Ruby depending on the type
of the thing present in the when clause, calls the respective .=== method.
Say when statement contains the class names, then the rule is - it will use Module#===, which will return true if the right side is an instance of,
or subclass of, the left side. One example with this context is:
Here instance of test occurs
obj = 'hello'
#=> "hello"
case obj
when String
print 'It is a string'
when Fixnum
print 'It is a number'
else
print 'It is not a string'
end
#It is a string
#=> nil
Here subclass of test occurs
num = 10
#=> 10
case num
when Numeric
puts "Right class"
else
puts "Wrong class"
end
#Right class
#=> nil
Now when contains String literals then String#=== is called, which in turn checks if left and right handside
literal are same(same chracter in same sequence) or not.
a = "abc"
#=> "abc"
case a
when "def" then p "Hi"
when "abc" then p "found"
else "not found"
end
#"found"
#=> "found"
The all logic is too cool. Now my query is with case/when structure -
How does ruby know if when holding class, or String literals or
anything valid at runtime?
or
What test does it perform before calling the respective .===
operator on the thing when holding currently.
EDIT
Before understanding the Case/when working principal,let me clear the below which when does while it gets its turn.
String.===("abc") #=> true
Because "abc" is an instance of String class. - Am I right?
Now I tried the below just to check who is whose super class.
10.class #=> Fixnum
Fixnum.superclass #=> Integer
Integer.superclass #=> Numeric
Numeric.superclass #=> Object
Humm. That means the below returns true as Fixnum is also the indirect subclass of Numeric. - Am I right?
Numeric.===(10) #=> true
But why then the below outputs contradictory to the above?
Numeric.===(Fixnum) #=> false
Trying to be more specific to my query as below :
When we are calling Numeric.===(10) and String.===("abc") . I think we are sending not "abc" and 10 rather "abc".class and 10.class.
10.===(10) #=> true
Numeric.===(10) #=> true
Now look at the above. Both return true. Does they output true on the same logic? I think NO. 10.===(10) is just like
10 ==(10) comparison. But Numeric.===(10) outputs true as class of 10 is the subclass of Numeric.
"abc".===("abc") #=> true
String.===("abc") #=> true
Now look at the above. Both return true. Does they output true on the same logic? I think NO. "abc".===("abc") is just like simple string literal comparison "abc" ==("abc") comparison. But String.===("abc") outputs true as "abc" which is an instance of String.
Now my question is how ruby detects lefthand side operands types and apply the proper rule of comparisons ?
I might be 100% wrong, In that case please correct me.
I'll try to explain what #Lee Jarvis also explaines.
class Someclass
end
s = Someclass.new
p s.methods.sort
#[:!, :!=, :!~, :<=>, :==, :===, :=~, :__id__, :__send__, :class, :clone,(...)
Look at the 5th method. My Someclass instance has a ===method out of nowhere.
Actually, it has 56 methods and I did not define one of them. They are inherited from Object; every class in Ruby inherits from Object. For class Object (and my Someclass), #=== is effectively the same as calling #==, but (as the docs say) #=== is typically overwritten by descendants to provide meaningful semantics in case statements.
So ruby does nothing smart, it just sends the object in question a === message (or calls the ===method if you prefer that).
Now my question is how ruby detects lefthand side operands types and apply the proper rule of comparisons ?
It doesn't. It simply calls the === method. That's it. That's just how object-orientation works, and it is nothing specific to Ruby, every OO language works the same way: you call a method on an object, and the object decides how to react. (Or, in a class-based language like Ruby, PHP, Java, C#, C++, Python etc. the class of the object decides.)
Different objects may react in different ways. Classes check whether the argument is an instance of themselves, Regexps check whether the argument is matched by them, Ranges check whether the argument is covered by them.
It's just basic method dispatch.
Some people say that the bang ! indicates the method "may" modify the object.
In this example:
foo = "A STRING"
foo.downcase
puts foo
# => A STRING
foo.downcase!
puts foo
# => a string
the bang symbol appears to be causing a side effect. Is obj.method! equivalent to obj = obj.method? If not, why? Is there some method for which these two expressions are not equivalent?
The bang ! in a method name means "danger" in general.
It is a method naming convention, not an operator, not a rule.
It typically means warning, or mutation, or irreversable, or raises an exception, etc.
We often use "!" as a naming convention to distiguish two similar methods:
The normal method name returns a result.
The "!" method name does something dangerous.
Made up examples of "!" meaning mutation:
obj.change #=> return a new object that has the change.
obj.change! #=> do the change by mutating the object.
Made up examples of "!" meaning "raises an error":
obj.save #=> if the object can't save, then return false.
obj.save! #=> if the object can't save, then raise an exception.
Made up examples of "!" to mean "DANGER" or "cannot be undone":
obj.exit #=> exit normally, running all exit handlers.
obj.exit! #=> exit immediately, skipping all exit handlers.
All of these are just naming conventions, where the developer has chosen to provide two similarly-named methods.
What's the science behind the fact that the to_i method in Ruby's NilClass instances returns zero? Returning nil or raising an exception would not be more logical?
It fits the Ruby philosophy of permissiveness (as opposed to, for example, the strictness of Python):
nil.to_i #=> 0
"".to_i #=> 0
"123hello".to_i #=> 123
"hello".to_i #=> 0
As noted by Zabba, you can use Kernel#Integer(string) for strict conversion.
NilClass defines #to_i for the same reason it defines a #to_a that returns []. It's giving you something of the right type but an empty sort of value.
This is actually quite useful. For example:
<%= big.long.expr.nil? ? "" : big.long.expr %>
becomes:
<%= big.long.expr %>
Much nicer! (Erb is calling #to_s which, for nil, is "".) And:
if how.now.brown.cow && how.now.brown.cow[0]
how.now.brown.cow[0]
else
0
end
becomes:
how.now.brown.cow.to_a[0].to_i
The short conversions exist when only a representation is needed. The long conversions are the ones that the Ruby core methods call and they require something very close. Use them if you want a type check.
That is:
thing.to_int # only works when almost Integer already. NilClass throws NoMethodError
thing.to_i # this works for anything that cares to define a conversion
to_i means "convert me to an integer if you can".
If you want "if you're very much integer-like, give me your integer value, else give a NoMethodError", then use .to_int.
There's another question that asks about the difference between to_i and to_int, to_s versus to_str, etc. Let me know if you'd like me to find it for you.
The protocol of to_i says that you must return an Integer and you must not raise an exception. Both of your suggestions violate at least one of those rules. So, no, those would not only not be more logical, they would be simply invalid.
Note, however, that nil does not respond to to_int. If it did respond to to_int, that would, indeed, be "illogical".
If you happen to be in Rails then you can use try:
nil.to_i # => 0
nil.try :to_i # => nil
A concise Ruby 2.3+ method for returning nil for nil.to_i instead of 0, is to use the safe navigation operator:
irb(main):001:0> nil.to_i
=> 0
irb(main):002:0> nil&.to_i
=> nil