Why do I get different results in IRB and a script? - ruby

Is there a difference in how IRB and Ruby execute some expressions?
These expressions give different results in IRB and when run from the command line. The question is, which one is correct?
IRB:
>> s = 'hello'
=> "hello"
>> s.size
>> s[s.length] = '!'
IndexError: index 5 out of string
from (irb):31:in `[]='
from (irb):31
>>
And in the normal script:
s = 'hello'
s[s.length] = '!'
puts s
laptop user$ ./prgruby.rb
hello!

Here is the doc of String#[] for 1.8.7 :
str[fixnum] = fixnum
The forms that take a Fixnum will raise an IndexError if the value is
out of range
Here is the same doc for 1.9.3 : the same definition is present
After test, what happen in Ruby 1.9.3 is s.length is not out of range for assignation. This make sense at it is the end of the string : you do not have to arbitrary fill the missing indexes but I guess it may be or should be documented somewhere ?

Related

Can't change Strings into int in `[]`

In ruby, I try to convert a String to Int in operator '[]' But failed.
Here is the code (my input is 14 45):
STDIN.gets.split(/\s+/).each do |str|
book = tags[str.to_i] # book is just a new variable. tags is an array
end
the ruby will stop with an error:
in '[]': no implicit conversion of String into Integer (TypeError)
So I change my code to follows(this one works well.):
STDIN.gets.split(/\s+/).each do |str|
number = str.to_i # for converting
book = tags[number]
end
This one works well. But I must add one more line for converting. Is there a good way for avoiding that line?
my version of ruby is: $: ruby --version ==> ruby 2.0.0p0 (2013-02-24 revision39474) [i686-linux]
Hi Guys, Please let me KNOW why you still want to close this topic. THANKS.
The error message you are getting will definitely only ever occur when you pass a String as an index to Array#[]. So you are probably not showing us the source that you are actually running. Consider:
a = [1,2,3]
str = 'string'
str.to_i
#=> 0
a[str.to_i]
#=> 1
number = str.to_i
#=> 0
a[number]
#=> 1
a['string']
# TypeError: no implicit conversion of String into Integer
By the way, the error message in your question is specific to Ruby 2.0.0:
RUBY_VERSION
#=> "2.0.0"
a = [1,2,3]
a['string']
# TypeError: no implicit conversion of String into Integer
Whereas in Ruby 1.9.3-p392 you will get this error message:
RUBY_VERSION
#=> "1.9.3"
a = [1,2,3]
a['string']
# TypeError: can't convert String into Integer

Lazy-evaluation of a "#{}"-string in ruby

I started to put print-statements throughout my code. So as not to clutter up the output, I did something like:
dputs LEVEL, "string"
where LEVEL is 0 for errors, 1 for important .. 5 for verbose and is compared to DEBUG_LEVEL. Now my problem is, that in a statement like:
dputs 5, "#{big_class.inspect}"
the string is always evaluated, also if I set DEBUG_LEVEL to 1. And this evaluation can take a long time. My favourite solution would be something like:
dputs 5, '#{big_class.inspect}'
and then evaluate the string if desired. But I don't manage to get the string in a form I can evaluate. So the only think I could come up with is:
dputs( 5 ){ "#{big_class.inspect}" }
but this looks just ugly. So how can I evaluate a '#{}' string?
You could do this by having dputs use sprintf (via %). That way it can decide not to build the interpolated string unless it knows it's going to print it:
def dputs(level, format_str, *vars)
puts(format_str % vars) if level <= LEVEL
end
LEVEL = 5
name = 'Andrew'
dputs 5, 'hello %s', name
#=> hello Andrew
Or, as you suggest, you can pass a block which would defer the interpolation till the block actually runs:
def dputs(level, &string)
raise ArgumentError.new('block required') unless block_given?
puts string.call if level <= LEVEL
end
I think it's of no value whatsoever, but I just came up with:
2.3.1 :001 > s = '#{a}'
=> "\#{a}"
2.3.1 :002 > a = 1
=> 1
2.3.1 :003 > instance_eval s.inspect.gsub('\\', '')
=> "1"
2.3.1 :004 > s = 'Hello #{a} and #{a+1}!'
=> "Hello \#{a} and \#{a+1}!"
2.3.1 :005 > instance_eval s.inspect.gsub('\\', '')
=> "Hello 1 and 2!"
Don't use that in production :)
I don't think you can dodge the ugly there. The interpolation happens before the call to dputs unless you put it inside a block, which postpones it until dputs evaluates it. I don't know where dputs comes from, so I'm not sure what its semantics are, but my guess is the block would get you the lazy evaluation you want. Not pretty, but it does the job.
OK, obviously I was just too lazy. I thought there must be a more clean way to do this, Ruby being the best programming language and all ;) To evaluate a string like
a = '#{1+1} some text #{big_class.inspect}'
only when needed, I didn't find a better way than going through the string and eval all "#{}" encountered:
str = ""
"#{b}\#{}".scan( /(.*?)(#\{[^\}]*\})/ ){
str += $1
str += eval( $2[2..-2] ).to_s
}
if you're not into clarity, you can get rid of the temporary-variable str:
"#{b}\#{}".scan( /(.*?)(#\{[^\}]*\})/ ).collect{|c|
c[0] + eval( c[1][2..-2] ).to_s
}.join
The String.scan-method goes through every '#{}'-block, as there might be more than one, evaluating it (the 2..-2 cuts out the "#{" and "}") and putting it together with the rest of the string.
For the corner-case of the string not ending with a '#{}'-block, an empty block is added, just to be sure.
But well, after being some years in Ruby, this still feels clunky and C-ish. Perhaps it's time to learn a new language!

Remove substring from the string

I am just wondering if there is any method to remove string from another string?
Something like this:
class String
def remove(s)
self[s.length, self.length - s.length]
end
end
You can use the slice method:
a = "foobar"
a.slice! "foo"
=> "foo"
a
=> "bar"
there is a non '!' version as well. More info can be seen in the documentation about other versions as well:
http://www.ruby-doc.org/core/classes/String.html#method-i-slice-21
How about str.gsub("subString", "")
Check out the Ruby Doc
If it is a the end of the string, you can also use chomp:
"hello".chomp("llo") #=> "he"
If you only have one occurrence of the target string you can use:
str[target] = ''
or
str.sub(target, '')
If you have multiple occurrences of target use:
str.gsub(target, '')
For instance:
asdf = 'foo bar'
asdf['bar'] = ''
asdf #=> "foo "
asdf = 'foo bar'
asdf.sub('bar', '') #=> "foo "
asdf = asdf + asdf #=> "foo barfoo bar"
asdf.gsub('bar', '') #=> "foo foo "
If you need to do in-place substitutions use the "!" versions of gsub! and sub!.
Ruby 2.5+
If your substring is at the beginning of in the end of a string, then Ruby 2.5 has introduced the methods for this:
delete_prefix for removing a substring from the beginning of the string
delete_suffix for removing a substring from the end of the string
If you are using Rails there's also remove.
E.g. "Testmessage".remove("message") yields "Test".
Warning: this method removes all occurrences
If you are using rails or at less activesupport you got String#remove and String#remove! method
def remove!(*patterns)
patterns.each do |pattern|
gsub! pattern, ""
end
self
end
source: http://api.rubyonrails.org/classes/String.html#method-i-remove
If I'm interpreting right, this question seems to ask for something like a minus (-) operation between strings, i.e. the opposite of the built-in plus (+) operation (concatenation).
Unlike the previous answers, I'm trying to define such an operation that must obey the property:
IF c = a + b THEN c - a = b AND c - b = a
We need only three built-in Ruby methods to achieve this:
'abracadabra'.partition('abra').values_at(0,2).join == 'cadabra'.
I won't explain how it works because it can be easily understood running one method at a time.
Here is a proof of concept code:
# minus_string.rb
class String
def -(str)
partition(str).values_at(0,2).join
end
end
# Add the following code and issue 'ruby minus_string.rb' in the console to test
require 'minitest/autorun'
class MinusString_Test < MiniTest::Test
A,B,C='abra','cadabra','abracadabra'
def test_C_eq_A_plus_B
assert C == A + B
end
def test_C_minus_A_eq_B
assert C - A == B
end
def test_C_minus_B_eq_A
assert C - B == A
end
end
One last word of advice if you're using a recent Ruby version (>= 2.0): use Refinements instead of monkey-patching String like in the previous example.
It is as easy as:
module MinusString
refine String do
def -(str)
partition(str).values_at(0,2).join
end
end
end
and add using MinusString before the blocks where you need it.
here's what I'd do
2.2.1 :015 > class String; def remove!(start_index, end_index) (end_index - start_index + 1).times{ self.slice! start_index }; self end; end;
2.2.1 :016 > "idliketodeleteHEREallthewaytoHEREplease".remove! 14, 32
=> "idliketodeleteplease"
2.2.1 :017 > ":)".remove! 1,1
=> ":"
2.2.1 :018 > "ohnoe!".remove! 2,4
=> "oh!"
Formatted on multiple lines:
class String
def remove!(start_index, end_index)
(end_index - start_index + 1).times{ self.slice! start_index }
self
end
end

Why does Ruby need to force an Array into string by using "to_a" if it is clear it is a String addition?

For example:
ruby-1.9.2-p0 > a = ['hello', 'world']
=> ["hello", "world"]
ruby-1.9.2-p0 > "foo" + a
TypeError: can't convert Array into String
from (irb):3:in `+'
from (irb):3
from /Users/peter/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
ruby-1.9.2-p0 > "foo" + a.to_s
=> "foo[\"hello\", \"world\"]"
ruby-1.9.2-p0 > puts "foo" + a.to_s
foo["hello", "world"]
why can't Ruby automatically convert the array to String?
It's not that Ruby can't, it's more that it won't. It's a strongly typed language, which means you need to take care of type conversions yourself. This is helpful in catching errors early that result from mixing incompatible types, but requires a little more care and typing from the programmer.
Strings are a special case because you can use string interpolation to call to_s implicitly:
obj = Object.new.tap {|o|
def o.to_s
'object!'
end
def o.inspect
'[object]'
end
}
"foo: " + obj # TypeError
"foo: #{obj}"
=> "foo: object!"
I redefined inspect to show that to_s is being called, and not inspect. On ruby 1.9, Object#inspect calls to_s, so if I didn't redefine inspect, the above code wouldn't show clearly which method was actually being called during interpolation.
You can make your Ruby automatically convert to String.
class Array
def to_string
self.unshift("").join(" ")
end
end
a = ["Hello", "World"]
"foo" + a.to_string
I used Ruby for a bit before Rails came out. I just downloaded it again and was playing around, then saw your question.
I may be ignorant or a nutter, but hey... that's my stab at it.

How does Ruby 1.9 handle character cases in source code?

In Ruby 1.8 and earlier,
Foo
is a constant (a Class, a Module, or another constant). Whereas
foo
is a variable. The key difference is as follows:
module Foo
bar = 7
BAZ = 8
end
Foo::BAZ
# => 8
Foo::bar
# NoMethodError: undefined method 'bar' for Foo:Module
That's all well and good, but Ruby 1.9 allows UTF-8 source code. So is ℃ "uppercase" or "lowecase" as far as this is concerned? What about ⊂ (strict subset) or Ɖfoo?
Is there a general rule?
Later:
Ruby-core is already considering some of the mathematical operators. For example
module Kernel
def √(num)
...
end
def ∑(*args)
...
end
end
would allow
x = √2
y = ∑(1, 45, ...)
I would love to see
my_proc = λ { |...| ... }
x ∈ my_enumerable # same as my_enumerable.include?(x)
my_infinite_range = (1..∞)
return 'foo' if x ≠ y
2.21 ≈ 2.2
OK, my joking answer didn't go down so well.
This mailing list question, with answer from Matz indicates that Ruby 1.9's built in String#upcase and String#downcase methods will only handle ASCII characters.
Without testing it myself, I would see this as strong evidence that all non-ascii characters in source code will likely be considered lowercase.
Can someone download and compile the latest 1.9 and see?
I don't know what ruby would do if you used extended UTF8 characters as identifiers in your source code, but I know what I would do, which would be to slap you upside the back of the head and tell you DON'T DO THAT
I would love to see
my_proc = λ { |...| ... }
x ∈ my_enumerable # same as my_enumerable.include?(x)
my_infinite_range = (1..∞)
return 'foo' if x ≠ y
2.21 ≈ 2.2
I would love to see someone trying to type that program on an English keyboard :P
In Ruby 1.9.2-p0 (YARV) the result is the same as in the original post (i.e., Foo::bar #=> # NoMethodError: undefined method 'bar' for Foo:Module). Also, letters with accent are unfortunately not considered as being upper nor lower and related methods produce no result.
Examples:
"á".upcase
=> "á"
"á" == "Á".downcase
=> false
I can't get IRB to accept UTF-8 characters, so I used a test script (/tmp/utf_test.rb).
"λ" works fine as a variable name:
# encoding: UTF-8
λ = 'foo'
puts λ
# from the command line:
> ruby -KU /tmp/utf_test.rb
foo
"λ" also works fine as a method name:
# encoding: UTF-8
Kernel.class_eval do
alias_method :λ, :lambda
end
(λ { puts 'hi' }).call
# from the command line:
> ruby -KU /tmp/utf_test.rb:
hi
It doesn't work as a constant, though:
# encoding: UTF-8
Object.const_set :λ, 'bar'
# from the command line:
> ruby -KU /tmp/utf_test.rb:
utf_test.rb:2:in `const_set': wrong constant name λ (NameError)
Nor does the capitalized version:
# encoding: UTF-8
Object.const_set :Λ, 'bar'
# from the command line:
> ruby -KU /tmp/utf_test.rb:
utf_test.rb:2:in `const_set': wrong constant name Λ (NameError)
My suspicion is that constant names must start with a capital ASCII letter (must match /^[A-Z]/).

Resources