开发者

Ruby: String no longer mixes in Enumerable in 1.9

开发者 https://www.devze.com 2022-12-20 09:44 出处:网络
So how can I still be able to write beautiful code such as: \'im a string meing!\'.p开发者_StackOverflowop

So how can I still be able to write beautiful code such as:

'im a string meing!'.p开发者_StackOverflowop

Note: str.chop isn't sufficient answer


It is not what an enumerable string atually enumerates. Is a string a sequence of ...

  • lines,
  • characters,
  • codepoints or
  • bytes?

The answer is: all of those, any of those, either of those or neither of those, depending on the context. Therefore, you have to tell Ruby which of those you actually want.

There are several methods in the String class which return enumerators for any of the above. If you want the pre-1.9 behavior, your code sample would be

'im a string meing!'.bytes.to_a.pop

This looks kind of ugly, but there is a reason for it: a string is a sequence. You are treating it as a stack. A stack is not a sequence, in fact it pretty much is the opposite of a sequence.


That's not beautiful :)

Also #pop is not part of Enumerable, it's part of Array.

The reason why String is not enumerable is because there are no 'natural' units to enumerate, should it be on a character basis or a line basis? Because of this String does not have an #each

String instead provides the #each_char and #each_byte and #each_line methods for iteration in the way that you choose.


Since you don't like str[str.length], how about

'im a string meing!'[-1]  # returns last character as a character value

or

'im a string meing!'[-1,1]  # returns last character as a string

or, if you need it modified in place as well, while keeping it an easy one-liner:

class String
  def pop
    last = self[-1,1]
    self.chop!
    last
  end
end


#!/usr/bin/ruby1.8

s = "I'm a string meing!"
s, last_char = s.rpartition(/./)
p [s, last_char]    # => ["I'm a string meing", "!"]

String.rpartition is new for 1.9 but it's been back-ported to 1.8.7. It searches a string for a regular expression, starting at the end and working backwards. It returns the part of the string before the match, the match, and the part of the string after the match (which we discard here).


String#slice! and String#insert is going to get you much closer to what you want without converting your strings to arrays.

For example, to simulate Array#pop you can do:

text = '¡Exclamation!'
mark = text.slice! -1

mark == '!'          #=> true
text                 #=> "¡Exclamation"

Likewise, for Array#shift:

text = "¡Exclamation!"
inverted_mark = text.slice! 0

inverted_mark == '¡' #=> true
text                 #=> "Exclamation!"

Naturally, to do an Array#push you just use one of the concatenation methods:

text = 'Hello'
text << '!'          #=> "Hello!"
text.concat '!'      #=> "Hello!!"

To simulate Array#unshift you use String#insert instead, it's a lot like the inverse of slice really:

text = 'World!'
text.insert 0, 'Hello, ' #=> "Hello, World!"

You can also grab chunks from the middle of a string in multiple ways with slice.

First you can pass a start position and length:

text = 'Something!'
thing = text.slice 4, 5

And you can also pass a Range object to grab absolute positions:

text = 'This is only a test.'
only = text.slice (8..11)

In Ruby 1.9 using String#slice like this is identical to String#[], but if you use the bang method String#slice! it will actually remove the substring you specify.

text = 'This is only a test.'
only = text.slice! (8..12)
text == 'This is a test.'      #=> true

Here's a slightly more complex example where we reimplement a simple version of String#gsub! to do a search and replace:

text = 'This is only a test.'
search = 'only'
replace = 'not'

index = text =~ /#{search}/
text.slice! index, search.length
text.insert index, replace

text == 'This is not a test.'  #=> true

Of course 99.999% of the time, you're going to want to use the aforementioned String.gsub! which will do the exact same thing:

text = 'This is only a test.'
text.gsub! 'only', 'not' 

text == 'This is not a test.'  #=> true

references:

  • Ruby String Documentation
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号