1

Sorry for the noob question, but what does this asterisk mean at the beginning of a range?

class Matrix
  def initialize(matrix_string)
    @matrix = matrix_string.split("\n").map do |row|
        row.split.map(&:to_i)
    end
    @rows = rows.length
    @cols = columns.length
  end

  def rows
    @matrix
  end

  def columns
    @matrix.transpose
  end

  # --->>***
  def saddle_points
    [*0...@rows].product([*0...@cols]).select do |coords|
        saddle_point?(*coords)
  end
  # ***<----
end

private
  def   saddle_point?(row, col)
    (@matrix[row][col] == rows[row].max) && 
    (@matrix[row][col] == columns[col].min)
  end
end
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160

3 Answers3

3

As it is stated in the documentation:

You can turn an Array into an argument list with * (or splat) operator:

arguments = [1, 2, 3]
my_method(*arguments)

The same might be done for Range:

arguments = 1..3
my_method(*arguments) # essentially the same as my_method(1, 2, 3)

Also splat operator is allowed before ranges inside the array declaration to implicitly convert Range to Array:

[*1..3]
#⇒ [1, 2, 3]
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
1

* unpacks an array by "splatting" its contents and converting it to an array.

Alec
  • 8,529
  • 8
  • 37
  • 63
1

As stated by the other answers, the * operator is used to turn an array into an argument list.

But what if the object is not an array, as in your case? Then Ruby will call #to_a on the object, if defined, and use the returned array instead. Otherwise, the object itself is used.

Here's the case where the object is neither an array nor does it define #to_a:

x = *4 # => [4]

If the object defines #to_a, it will be called and used, as in the case of Range:

x = *0..1 # => [0, 1]

To prove this, we can prepend a module to trace the call to #to_a:

module Trace
  def to_a
    puts "#to_a called"
    super
  end
end

Range.prepend(Trace)

x = *0..1
# prints "#to_a called"
# => [0, 1]

Note that Ruby will not call #to_a if your object already is of type Array.

We can use this on our custom types as well:

class Foo
  def to_a
    [1, 2, 3]
  end
end

x = *Foo.new # => [1, 2, 3]

By the way, Ruby's nil also implements #to_a. This allows us to pass nil as an argument as if nothing was passed, because nil.to_a returns []:

def count(*args)
  args.count
end

count # => 0
count(*0..1) # => 2
count(*nil) # => 0

This can be useful when you have a variable that could be nil that you pass to a method which has a default value:

def say_hi(name = "Jane")
  puts "Hi, #{name}"
end

name = nil
say_hi(*name) # prints "Hi, Jane"

But if we remove NilClass#to_a:

NilClass.undef_method(:to_a)

say_hi(*name) # prints "Hi, "
fphilipe
  • 9,739
  • 1
  • 40
  • 52