15

I'm working my way through Project Euler, and ran into a slightly surprising omission: There is no String#shift, unshift, push, or pop. I had assumed a String was considered a "sequential" object like an Array, since they share the ability to be indexed and iterated through, and that this would include the ability to easily change the beginning and ends of the object.

I know there are ways to create the same effects, but is there a specific reason that String does not have these methods?

wersimmon
  • 2,809
  • 3
  • 22
  • 35
  • 1
    probably because if String had those methods, people would use them. And that would be a bad thing. See also: "Pit of Success." You don't want the easiest way to also be the wrong way. – Jimmy Feb 10 '11 at 23:31
  • Why would it be a bad thing ? – Zabba Feb 10 '11 at 23:32
  • @Zabba: In a lot of languages strings are somewhat immutable. This lets you get away with tricks like intern-tables, but also means if you insist on mutating a string a lot, it's grossly inefficient. The classic example is the .NET "gotcha" interview question of all time "What is wrong with the following code?" (it's something like `bar = ""; foreach(var foo in frob) bar += foo;`) – Jimmy Feb 10 '11 at 23:45
  • Turns out similar questions have been asked before: http://stackoverflow.com/questions/2608493/why-did-matz-choose-to-make-strings-mutable-by-default-in-ruby and http://stackoverflow.com/questions/2266534/ruby-string-no-longer-mixes-in-enumerable-in-1-9. And try this out: `"miracle".each{|char| puts "howzzat#{char}"}` – Zabba Feb 11 '11 at 00:43

4 Answers4

12

Strings don't act as an enumerable object as of 1.9, because it's considered too confusing to decide what it'd be a list of:

  • A list of characters / codepoints?
  • A list of bytes?
  • A list of lines?
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
  • Ah, that's a good point. Was there ever a discussion in core about this, or was it just decided on as things progressed? – wersimmon Feb 11 '11 at 03:19
  • See http://stackoverflow.com/questions/2266534/ruby-string-no-longer-mixes-in-enumerable-in-1-9/2266616#2266616 – Zabba Feb 11 '11 at 21:50
5

Not being a Ruby contributor, I can't speak to their design goals, but from experience, I don't think that strings are regarded as 'sequential' objects; they're mutable in ways that suggest sequential behaviour, but most of the time they're treated atomically.

Case in point: in Ruby 1.9, String no longer mixes in Enumerable.

dnch
  • 9,565
  • 2
  • 38
  • 41
  • Looks like Matz himself removed `include Enumerable` from String back in 2006, [according to the github mirror](https://github.com/ruby/ruby/commit/4e37427ee5ecabda3e59ffd3ad1a6c85d4d9327b). – wersimmon Feb 10 '11 at 23:58
5
>> mystring = "abcdefgh"
=> "abcdefgh"
>> myarray = mystring.split("")
=> ["a", "b", "c", "d", "e", "f", "g", "h"]
>> myarray.pop
=> "h"
>> mystring = myarray.join
=> "abcdefg"

this should do it, you wouldhave to convert it to an array, and then back though

UPDATE:

use String#chop! and Stirng#<<

>> s = "abc"
=> "abc"
>> s.chop!
=> "ab"
>> s
=> "ab"
>> s<<"def"
=> "abdef"
>> s
=> "abdef"
>> 
loosecannon
  • 7,683
  • 3
  • 32
  • 43
  • I know you can turn it into an Array, as well as using #slice, #<<, and #+= to fake the functionality; I'm more concerned about why Strings don't have these methods in the first place. – wersimmon Feb 10 '11 at 23:49
  • Because they shouldn't, they are a string of characters, not an list or sequential object. If you need a list or queue or dqueue or FIFO object use an Array of characters. While they support some methods like that that doesnt mean that they should be sued like that all the time -- See Dan Cheail's answer – loosecannon Feb 10 '11 at 23:53
2

Well at least in 1.9.2, you can deal with a string like an array.

ruby-1.9.2-p290 :001 > "awesome"[3..-1] => "some" 

So if you want to do a sort of character left shift, just use [1..-1]

ruby-1.9.2-p290 :003 > "fooh!"[1..-1] => "ooh!"
genkilabs
  • 2,966
  • 30
  • 36