eigenclass logo
MAIN  Index  Search  Changes  PageRank  Login

Changes in Ruby 1.9, Oct. 07 update

This is a summary of the changes in Ruby 1.9 between Feb. and Oct. 07. As usual, refer to the full list for further details.

try_convert

The Array, Hash, String, IO and Regexp classes have a "try_convert" class method that either returns the converted value, as returned by to_ary, to_hash, to_string, to_io and to_regexp, or nil if the object cannot be converted for any reason.

Enumerable

each_with_index

Now forwards arguments to #each:

 [RUBY_VERSION, RUBY_RELEASE_DATE]                 # => ["1.9.0", "2007-08-03"]
 class X
   include Enumerable
   def each(*args); yield args.inspect end
 end
 z = nil
 X.new.each_with_index(42){|x| z = x}
 z                                                   # => ["[42]", 1]

cycle

Calls the given block for each element of the enumerable in a never-ending cycle:

a = ["a", "b", "c"]
a.cycle {|x| puts x }  # print, a, b, c, a, b, c,.. forever.

Enumerable#cycle can be described in Ruby as follows:

 def cycle
   a = []
   each{|x| a << x; yield x}
   loop{ a.each{|x| yield x} }
 end

take

Returns either the first n elements from the enumeration or all elements until the given block returns false (ruby-dev:30407):

a = [1, 2, 3, 4, 5]
 
a.take(3)             # => [1, 2, 3]
a.take {|i| i < 3 }   # => [1, 2]

drop

Without a block, returns an array with all but the first n elements from the enumeration. Otherwise drops elements while the block returns true (and returns all the elements after it returns a false value) (ruby-dev:30407):

a = [1, 2, 3, 4, 5]
 
a.drop(3)             # => [4, 5]
a.drop {|i| i < 3 }   # => [3, 4, 5]

inject (#reduce) without a block

If no block is given, the first argument to #inject is the name of a two-argument method that will be called; the optional second argument is the initial value:

[RUBY_VERSION, RUBY_RELEASE_DATE]                 # => ["1.9.0", "2007-08-03"]
(1..10).reduce(:+)                                # => 55

zip

Doesn't convert the arguments to arrays; enumerators are used instead.

minmax and minmax_by

Returns both the minimun and the maximum at once as a two-element array.

rewind

Rewinds the enumeration sequence.

Array

combination

ary.combination(n){|c| ...} 

yields all the combinations of length n of the elements in the array to the given block. If no block is passed, it returns an enumerator instead. The order of the combinations is unspecified.

 a = [1, 2, 3, 4]
 a.combination(1).to_a  #=> [[1],[2],[3],[4]]
 a.combination(2).to_a  #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
 a.combination(3).to_a  #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
 a.combination(4).to_a  #=> [[1,2,3,4]]
 a.combination(0).to_a  #=> [[]]: one combination of length 0
 a.combination(5).to_a  #=> []  : no combinations of length 5

ruby-list:42671

permutation

ary.permutation(n){|c| ...}

Operates like #combination, but with permutations of length n.

 a = [1, 2, 3]
 a.permutation(1).to_a  #=> [[1],[2],[3]]
 a.permutation(2).to_a  #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]
 a.permutation(3).to_a  #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
 a.permutation(0).to_a  #=> [[]]: one permutation of length 0
 a.permutation(4).to_a  #=> []  : no permutations of length 4

product

Returns the cartesian product of the receiver and the arrays given as arguments:

 [1,2,3].product([4,5])     # => [[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]]
 [1,2].product([1,2])       # => [[1,1],[1,2],[2,1],[2,2]]
 [1,2].product([3,4],[5,6]) # => [[1,3,5],[1,3,6],[1,4,5],[1,4,6],
                            #     [2,3,5],[2,3,6],[2,4,5],[2,4,6]]
 [1,2].product()            # => [[1],[2]]
 [1,2].product([])          # => []

Hash

Semantics for Hash#each and Hash#each_pair

each_pair passes two arguments to the block, #each passes a two-element array to the block, so

 [RUBY_VERSION, RUBY_RELEASE_DATE]                 # => ["1.9.0", "2007-08-03"]
 {1 => 2}.each{|x| p x}
 {1 => 2}.each_pair{|x| p x}
 # >> [1, 2]
 # >> 1

Keep in mind that |x| is equivalent to |x,|.

select

Returns a hash instead of an association array (ruby-core:11504):

[RUBY_VERSION, RUBY_RELEASE_DATE]                 # => ["1.8.5", "2006-08-25"]
{'foo'=>'bar','baz'=>'qux'}.select{|k,v| k=='baz' }    # => [["baz", "qux"]]

vs.

[RUBY_VERSION, RUBY_RELEASE_DATE]                 # => ["1.9.0", "2007-08-03"]
{'foo'=>'bar','baz'=>'qux'}.select{|k,v| k=='baz' }    # => {"baz"=>"qux"}

Strings and symbols

Encoding-awareness; it has finally happened.

String methods operate on chars and are thus encoding-aware. These are some of the affected methods:

  • ==
  • [] []=
  • each_line, each_char
  • hash
  • inspect
  • length
  • ljust, rjust, center
  • reverse
  • split
  • strip!, strip, lstrip, rstrip
  • succ
  • upcase, downcase, capitalize, swapcase

The length field in the format specifier for printf-like methods also operates on a character basis.

String#each_char

Passes each character (not byte!) to the given block.

String#encoding

Returns the encoding of the string.

String#force_encoding

Changes the encoding of the string to the given one and returns self.

String#subseq

Operates like [] but with a byte offset and count instead of using characters.

String#upto

Takes an optional second array specifying whether the to exclude the final value (by default, it is included).

Symbol#intern

Returns self.

Symbol#encoding

Returns the encoding of the symbol.

Symbol methods similar to those in String

Now Symbols respond to many methods that resemble those in String:

  • []
  • <=>, casecmp, =~, ===
  • empty?
  • encoding
  • length, size
  • match
  • slice
  • succ, next
  • upcase, downcase, capitalize, swapcase

Regexp#match, String#match

If a block is given, it will only be evaluated and given the matchdata if there is a match.

IO

IO#getbyte, IO#readbyte, StringIO#getbyte, StringIO#readbyte

getbyte returns either a Fixnum or nil on EOF. readbyte returns a Fixnum or raises EOFError.

IO#ungetc, StringIO#ungetc

Allows to push back an arbitrarily large character.

Kernel#getc has been removed

Replaced by STDIN.getc.

Numeric#fdiv

An alias for quo (ruby-dev:30771).

New semantics for block arguments

|v| now works like the former |v,|:

[RUBY_VERSION, RUBY_RELEASE_DATE]                 # => ["1.8.5", "2006-08-25"]
def m; yield 1, 2; end
m{|v| v}                                          # => [1, 2] # !> multiple values for a block parameter (2 for 1)

vs.

[RUBY_VERSION, RUBY_RELEASE_DATE]                 # => ["1.9.0", "2007-08-03"]
def m; yield 1, 2; end
m{|v| v}                                          # => 1

Splats

They are also allowed in expressions like

[RUBY_VERSION, RUBY_RELEASE_DATE]                # => ["1.9.0", "2007-08-03"]
a = [1,2,3]
b = [4,5,6]
[*a, *b]                                         # => [1, 2, 3, 4, 5, 6]

printf-style formatted strings (%)

  • %c can print a one character String (as returned e.g. by ?c).
  • %u behaves like %d for negative values (ruby-core:11575)

BasicObject#funcall! is gone

It has been replaced by send! by popular demand.

Proc#lambda?

Returns whether the Proc has got "lambda semantics" or "block semantics".

Fiber: coroutines/micro-threads

Fiber#yield can be used to force a change of context amongst micro-threads. The API is being refined. ko1 has told me that matz has blessed Fibers, so they will be included in 1.9.1, even if the API is in a state of flux.



Clarifications? - Rick DeNatale (2007-10-12 (Fri) 16:04:39)

Enumerable#cycle

 It might be me, but the description is as clear as mud.

Enumerable#take

 shouldn't this say either while the block evaluates to true, or until the block evaluates to false?

Also Matz recently mentioned on either ruby or ruby-core that Hash maintains a list of keys by insertion order, and Hash#each will yield in that order.

mfp 2007-10-13 (Sat) 04:00:46

I have updated the description of #cycle. Hope it's clearer now. Also, good catch on the #take braino.

As for hashes & insertion order, it was indeed said on ruby-core:12542. I'll see if I can find further references on ruby-dev (I like to link to rationales and discussions about the implementation when possible) and add it to the list.

Thanks


Yuck - Daniel Berger (2007-10-12 (Fri) 18:53:47)

Can't say I like most of these additions. Thumbs down.


me neither - vruz (2007-10-12 (Fri) 19:16:57)

Whilst it's good to see the language evolving, I'd like to see more work on performance optimisation, and improving the process accepting patches that have been floating around for months if not years.


Enumerable#cycle - Grant Hutchins (2007-10-12 (Fri) 19:19:15)

The way I understand it, Enumberable#cycle is like the Enumerable#each that keeps on going (looping back to the beginning) until break is called.


Yuck what? - murphy (2007-10-13 (Sam) 05:38:51)

Daniel, what's the matter? Please explain.

mfp 2007-10-13 (Sat) 15:52:44

There's little to hate (or love) vehemently in this change list if you compare it with experiments that have been discarded like the new method lookup rules or the ->() block syntax. Maybe the M17N stuff or the block argument semantics?


Like most except for block arg passing - Jeff (2007-10-14 (Sun) 11:13:36)

I actually like almost all of the changes, except I'm really disappointed to see the new semantics for block arguments. Seems counter-intuitive to collapse the yielded values into an array when all I want is the (say) the first value.

Imagine if I write a block that accepts two parameters; then the implementor of the class that's doing the yield changes the behavior to yield three arguments instead of two. In 1.8, this will not break the existing code - the old block just won't "see" the new value, and probably that's not important. But now in 1.9, the old iterator will break because it will suddenly be receiving a two-element array for that last parameter.

Plus, it breaks a lot of code already written with 1.8.

Unless I'm missing something (I hope I am)?

Thanks for the write-up, though. Extremely helpful!

mfp 2007-10-15 (Mon) 11:49:18

You have 1.8 and 1.9 reversed :-) In 1.8, if you have foo{|x| ... x } and foo yields several values to the block, x will be an array holding them all. In 1.9, x will be the first value. As you said, this is better in the sense that it allows you to yield additional arguments without affecting existent code, but it's 1.9 which gets it right, not 1.8. Note that in 1.8 you can do foo{|x,| x} (with a trailing comma) to get only the first value; I rarely see it used in practice, though.

Also, when the block takes more than one argument 1.8 and 1.9 behave identically:

def foo; yield 1, 2, 3 end
RUBY_VERSION             # => "1.8.5"
RUBY_RELEASE_DATE        # => "2006-08-25"
foo{|x,y| [x,y]}         # => [1, 2]
def foo; yield 1, 2, 3 end
RUBY_VERSION             # => "1.9.0"
RUBY_RELEASE_DATE        # => "2007-08-03"
foo{|x,y| [x,y]}         # => [1, 2]

Jeff 2007-10-19 (Fri) 12:00:17

I didn't believe you, so I opened up irb real quick, and sure enough - I was totally wrong. I did get 1.8 and 1.9 mixed up after all.

Thanks so much for the clarification. Jeff


Explaining "Yuck" - Daniel Berger (2007-10-15 (Mon) 13:03:57)

We do not need more methods in Array, Hash and Enumerable, at least not core. The Array class is already too tall, we're just making it worse, and these methods don't strike me as the 'everyday' sort of thing that belongs in the core. At best, they should be a stdlib addition.

The other changes seem marginal at best, confusing at worst, and are sucking away brain cycles from possibly more useful and fundamental improvements.


Re: "Explaining Yuck" - ta (2007-10-15 (Mon) 14:31:07)

Daniel,

I absolutely agree with you. It seems these latest sets of changes are simply variations on a theme that other methods already in existence can easily fill the gap for. I see absolutely no reason for this Enumerable#cycle method which can easily be wrapped around a loop{..} block anyway (as stated).

More time should be spent looking at the somewhat lagging performance issues Ruby is suffering from. It's lagging behind perl somewhat, for example.

Daniel Luz 2007-12-13 (Thr) 04:50:36

The big point about Enumerable#cycle is, I believe, the Enumerator:

colors = %w(White Silver).cycle
10.times do
  puts "Table row with color #{colors.next}"
end

The same enumerator can also be used with Enumerable#zip, for instance:

records.each.zip colors do |record, color|
  puts "Print record #{record} in color #{color}"
end

If you've already played with Python's iterators, generators and itertools, you may get the picture.


Another feature - murphy (2007-10-16 (Die) 13:38:47)

You forgot to mention taht Hashes are ordered now :)

mfp 2007-10-16 (Tue) 15:29:37

Yes; see Rick DeNatale's comment and my reply too.


String#subseq - WoNáDo (2007-10-30 (Die) 13:40:21)

As I recognized today by information from Matz (http://www.ruby-forum.com/topic/129798), the method String#subseq does not exist for Ruby usage. It's internal in the C-code of class String.

mfp 2007-10-30 (Tue) 13:52:16

You're very right, there's no rb_define_method for it. I'm removing it from the list.


Last modified:2007/10/12 12:47:48
Keyword(s):[blog] [frontpage] [ruby] [changelog] [1.9]
References: