[前][次][番号順一覧][スレッド一覧]

ruby-changes:53288

From: marcandre <ko1@a...>
Date: Sat, 3 Nov 2018 03:00:27 +0900 (JST)
Subject: [ruby-changes:53288] marcandRe: r65507 (trunk): lib/matrix.rb: Make Matrix & Vector mutable. Add #[]=, #map!.

marcandre	2018-11-03 02:52:51 +0900 (Sat, 03 Nov 2018)

  New Revision: 65507

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=65507

  Log:
    lib/matrix.rb: Make Matrix & Vector mutable. Add #[]=, #map!.
    
    Adapted from patch by Grzegorz Jakubiak. [#14151] [Fix GH-1769] [Fix GH-1905]

  Modified files:
    trunk/NEWS
    trunk/lib/matrix.rb
    trunk/test/matrix/test_matrix.rb
    trunk/test/matrix/test_vector.rb
Index: lib/matrix.rb
===================================================================
--- lib/matrix.rb	(revision 65506)
+++ lib/matrix.rb	(revision 65507)
@@ -302,12 +302,107 @@ class Matrix https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L302
   alias element []
   alias component []
 
+  #
+  # :call-seq:
+  #   matrix[range, range] = matrix/element
+  #   matrix[range, integer] = vector/column_matrix/element
+  #   matrix[integer, range] = vector/row_matrix/element
+  #   matrix[integer, integer] = element
+  #
+  # Set element or elements of matrix.
   def []=(i, j, v)
-    @rows[i][j] = v
+    raise FrozenError, "can't modify frozen Matrix" if frozen?
+    rows = check_range(i, :row) or row = check_int(i, :row)
+    columns = check_range(j, :column) or column = check_int(j, :column)
+    if rows && columns
+      set_row_and_col_range(rows, columns, v)
+    elsif rows
+      set_row_range(rows, column, v)
+    elsif columns
+      set_col_range(row, columns, v)
+    else
+      set_value(row, column, v)
+    end
   end
   alias set_element []=
   alias set_component []=
-  private :[]=, :set_element, :set_component
+  private :set_element, :set_component
+
+  # Returns range or nil
+  private def check_range(val, direction)
+    return unless val.is_a?(Range)
+    count = direction == :row ? row_count : column_count
+    CoercionHelper.check_range(val, count, direction)
+  end
+
+  private def check_int(val, direction)
+    count = direction == :row ? row_count : column_count
+    CoercionHelper.check_int(val, count, direction)
+  end
+
+  private def set_value(row, col, value)
+    raise ErrDimensionMismatch, "Expected a a value, got a #{value.class}" if value.respond_to?(:to_matrix)
+
+    @rows[row][col] = value
+  end
+
+  private def set_row_and_col_range(row_range, col_range, value)
+    if value.is_a?(Matrix)
+      if row_range.size != value.row_count || col_range.size != value.column_count
+        raise ErrDimensionMismatch, [
+          'Expected a Matrix of dimensions',
+          "#{row_range.size}x#{col_range.size}",
+          'got',
+          "#{value.row_count}x#{value.column_count}",
+        ].join(' ')
+      end
+      source = value.instance_variable_get :@rows
+      row_range.each_with_index do |row, i|
+        @rows[row][col_range] = source[i]
+      end
+    elsif value.is_a?(Vector)
+      raise ErrDimensionMismatch, 'Expected a Matrix or a value, got a Vector'
+    else
+      value_to_set = Array.new(col_range.size, value)
+      row_range.each do |i|
+        @rows[i][col_range] = value_to_set
+      end
+    end
+  end
+
+  private def set_row_range(row_range, col, value)
+    if value.is_a?(Vector)
+      Matrix.Raise ErrDimensionMismatch unless row_range.size == value.size
+      set_column_vector(row_range, col, value)
+    elsif value.is_a?(Matrix)
+      Matrix.Raise ErrDimensionMismatch unless value.column_count == 1
+      value = value.column(0)
+      Matrix.Raise ErrDimensionMismatch unless row_range.size == value.size
+      set_column_vector(row_range, col, value)
+    else
+      @rows[row_range].each{|e| e[col] = value }
+    end
+  end
+
+  private def set_column_vector(row_range, col, value)
+    value.each_with_index do |e, index|
+      r = row_range.begin + index
+      @rows[r][col] = e
+    end
+  end
+
+  private def set_col_range(row, col_range, value)
+    value = if value.is_a?(Vector)
+      value.to_a
+    elsif value.is_a?(Matrix)
+      Matrix.Raise ErrDimensionMismatch unless value.row_count == 1
+      value.row(0).to_a
+    else
+      Array.new(col_range.size, value)
+    end
+    Matrix.Raise ErrDimensionMismatch unless col_range.size == value.size
+    @rows[row][col_range] = value
+  end
 
   #
   # Returns the number of rows.
@@ -360,18 +455,50 @@ class Matrix https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L455
   #
   # Returns a matrix that is the result of iteration of the given block over all
   # elements of the matrix.
+  # Elements can be restricted by passing an argument:
+  # * :all (default): yields all elements
+  # * :diagonal: yields only elements on the diagonal
+  # * :off_diagonal: yields all elements except on the diagonal
+  # * :lower: yields only elements on or below the diagonal
+  # * :strict_lower: yields only elements below the diagonal
+  # * :strict_upper: yields only elements above the diagonal
+  # * :upper: yields only elements on or above the diagonal
   #   Matrix[ [1,2], [3,4] ].collect { |e| e**2 }
   #     => 1  4
   #        9 16
   #
-  def collect(&block) # :yield: e
-    return to_enum(:collect) unless block_given?
-    rows = @rows.collect{|row| row.collect(&block)}
-    new_matrix rows, column_count
+  def collect(which = :all, &block) # :yield: e
+    return to_enum(:collect, which) unless block_given?
+    dup.collect!(which, &block)
   end
   alias_method :map, :collect
 
   #
+  # Invokes the given block for each element of matrix, replacing the element with the value
+  # returned by the block.
+  # Elements can be restricted by passing an argument:
+  # * :all (default): yields all elements
+  # * :diagonal: yields only elements on the diagonal
+  # * :off_diagonal: yields all elements except on the diagonal
+  # * :lower: yields only elements on or below the diagonal
+  # * :strict_lower: yields only elements below the diagonal
+  # * :strict_upper: yields only elements above the diagonal
+  # * :upper: yields only elements on or above the diagonal
+  #
+  def collect!(which = :all)
+    return to_enum(:collect!, which) unless block_given?
+    raise FrozenError, "can't modify frozen Matrix" if frozen?
+    each_with_index(which){ |e, row_index, col_index| @rows[row_index][col_index] = yield e }
+  end
+
+  alias map! collect!
+
+  def freeze
+    @rows.freeze
+    super
+  end
+
+  #
   # Yields all elements of the matrix, starting with those of the first row,
   # or returns an Enumerator if no block given.
   # Elements can be restricted by passing an argument:
@@ -865,12 +992,11 @@ class Matrix https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L992
   end
 
   #
-  # Returns a clone of the matrix, so that the contents of each do not reference
-  # identical objects.
-  # There should be no good reason to do this since Matrices are immutable.
+  # Called for dup & clone.
   #
-  def clone
-    new_matrix @rows.map(&:dup), column_count
+  private def initialize_copy(m)
+    super
+    @rows = @rows.map(&:dup) unless frozen?
   end
 
   #
@@ -1562,6 +1688,26 @@ class Matrix https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L1688
     def self.coerce_to_matrix(obj)
       coerce_to(obj, Matrix, :to_matrix)
     end
+
+    # Returns `nil` for non Ranges
+    # Checks range validity, return canonical range with 0 <= begin <= end < count
+    def self.check_range(val, count, kind)
+      canonical = (val.begin + (val.begin < 0 ? count : 0))..
+                  (val.end ? val.end + (val.end < 0 ? count : 0) - (val.exclude_end? ? 1 : 0)
+                           : count - 1)
+      unless 0 <= canonical.begin && canonical.begin <= canonical.end && canonical.end < count
+        raise IndexError, "given range #{val} is outside of #{kind} dimensions: 0...#{count}"
+      end
+      canonical
+    end
+
+    def self.check_int(val, count, kind)
+      val = CoercionHelper.coerce_to_int(val)
+      if val >= count || val < -count
+        raise IndexError, "given #{kind} #{val} is outside of #{-count}...#{count}"
+      end
+      val
+    end
   end
 
   include CoercionHelper
@@ -1656,6 +1802,9 @@ end https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L1802
 # To access elements:
 # * #[](i)
 #
+# To set elements:
+# * #[]=(i, v)
+#
 # To enumerate the elements:
 # * #each2(v)
 # * #collect2(v)
@@ -1678,8 +1827,10 @@ end https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L1827
 # * #inner_product(v), dot(v)
 # * #cross_product(v), cross(v)
 # * #collect
+# * #collect!
 # * #magnitude
 # * #map
+# * #map!
 # * #map2(v)
 # * #norm
 # * #normalize
@@ -1758,7 +1909,11 @@ class Vector https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L1909
   # ACCESSING
 
   #
-  # Returns element number +i+ (starting at zero) of the vector.
+  # :call-seq:
+  #   vector[range]
+  #   vector[integer]
+  #
+  # Returns element or elements of the vector.
   #
   def [](i)
     @elements[i]
@@ -1766,12 +1921,44 @@ class Vector https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L1921
   alias element []
   alias component []
 
+  #
+  # :call-seq:
+  #   vector[range] = new_vector
+  #   vector[range] = row_matrix
+  #   vector[range] = new_element
+  #   vector[integer] = new_element
+  #
+  # Set element or elements of vector.
+  #
   def []=(i, v)
-    @elements[i]= v
+    raise FrozenError, "can't modify frozen Vector" if frozen?
+    if i.is_a?(Range)
+      range = Matrix::CoercionHelper.check_range(i, size, :vector)
+      set_range(range, v)
+    else
+      index = Matrix::CoercionHelper.check_int(i, size, :index)
+      set_value(index, v)
+    end
   end
   alias set_element []=
   alias set_component []=
-  private :[]=, :set_element, :set_component
+  private :set_element, :set_component
+
+  private def set_value(index, value)
+    @elements[index] = value
+  end
+
+  private def set_range(range, value)
+    if value.is_a?(Vector)
+      raise ArgumentError, "vector to be set has wrong size" unless range.size == value.size
+      @elements[range] = value.elements
+    elsif value.is_a?(Matrix)
+      Matrix.Raise ErrDimensionMismatch unless value.row_count == 1
+      @elements[range] = value.row(0).elements
+    else
+      @elements[range] = Array.new(range.size, value)
+    end
+  end
 
   # Returns a vector with entries rounded to the given precision
   # (see Float#round)
@@ -1868,6 +2055,20 @@ class Vector https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L2055
     all?(&:zero?)
   end
 
+  def freeze
+    @elements.freeze
+    super
+  end
+
+  #
+  # Called for dup & clone.
+  #
+  private def initialize_copy(v)
+    super
+    @elements = @elements.dup unless frozen?
+  end
+
+
   #--
   # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
   #++
@@ -1886,13 +2087,6 @@ class Vector https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L2087
   end
 
   #
-  # Returns a copy of the vector.
-  #
-  def clone
-    self.class.elements(@elements)
-  end
-
-  #
   # Returns a hash-code for the vector.
   #
   def hash
@@ -2043,6 +2237,17 @@ class Vector https://github.com/ruby/ruby/blob/trunk/lib/matrix.rb#L2237
   alias_method :map, :collect
 
   #
+  # Like Array#collect!
+  #
+  def collect!(&block)
+    return to_enum(:collect!) unless block_given?
+    raise FrozenError, "can't modify frozen Vector" if frozen?
+    @elements.collect!(&block)
+    self
+  end
+  alias map! collect!
+
+  #
   # Returns the modulus (Pythagorean distance) of the vector.
   #   Vector[5,8,2].r => 9.643650761
   #
Index: NEWS
===================================================================
--- NEWS	(revision 65506)
+++ NEWS	(revision 65507)
@@ -313,6 +313,14 @@ sufficient information, see the ChangeLo https://github.com/ruby/ruby/blob/trunk/NEWS#L313
 
     * Matrix#antisymmetric? / #skew_symmetric?
 
+    * Matrix#map! / #collect! [Feature #14151]
+
+    * Matrix#[]=
+
+    * Vector#map! / #collect!
+
+    * Vector#[]=
+
 [Net]
 
   [New options]
Index: test/matrix/test_vector.rb
===================================================================
--- test/matrix/test_vector.rb	(revision 65506)
+++ test/matrix/test_vector.rb	(revision 65507)
@@ -27,6 +27,108 @@ class TestVector < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/matrix/test_vector.rb#L27
     assert_raise(ArgumentError) { Vector.basis(index: 3) }
   end
 
+  def test_get_element
+    assert_equal(@v1[0..], [1, 2, 3])
+    assert_equal(@v1[0..1], [1, 2])
+    assert_equal(@v1[2], 3)
+    assert_equal(@v1[4], nil)
+  end
+
+  def test_set_element
+
+    assert_block do
+      v = Vector[5, 6, 7, 8, 9]
+      v[1..2] = Vector[1, 2]
+      v == Vector[5, 1, 2, 8, 9]
+    end
+
+    assert_block do
+      v = Vector[6, 7, 8]
+      v[1..2] = Matrix[[1, 3]]
+      v == Vector[6, 1, 3]
+    end
+
+    assert_block do
+      v = Vector[1, 2, 3, 4, 5, 6]
+      v[0..2] = 8
+      v == Vector[8, 8, 8, 4, 5, 6]
+    end
+
+    assert_block do
+      v = Vector[1, 3, 4, 5]
+      v[2] = 5
+      v == Vector[1, 3, 5, 5]
+    end
+
+    assert_block do
+      v = Vector[2, 3, 5]
+      v[-2] = 13
+      v == Vector[2, 13, 5]
+    end
+
+    assert_block do
+      v = Vector[4, 8, 9, 11, 30]
+      v[1..-2] = Vector[1, 2, 3]
+      v == Vector[4, 1, 2, 3, 30]
+    end
+
+    assert_raise(IndexError) {Vector[1, 3, 4, 5][5..6] = 17}
+    assert_raise(IndexError) {Vector[1, 3, 4, 5][6] = 17}
+    assert_raise(Matrix::ErrDimensionMismatch) {Vector[1, 3, 4, 5][0..2] = Matrix[[1], [2], [3]]}
+    assert_raise(ArgumentError) {Vector[1, 2, 3, 4, 5, 6][0..2] = Vector[1, 2, 3, 4, 5, 6]}
+    assert_raise(FrozenError) { Vector[7, 8, 9].freeze[0..1] = 5}
+  end
+
+  def test_map!
+    v1 = Vector[1, 2, 3]
+    v2 = Vector[1, 3, 5].freeze
+    v3 = Vector[].freeze
+    assert_equal Vector[1, 4, 9], v1.map!{|e| e ** 2}
+    assert_equal v1, v1.map!{|e| e - 8}
+    assert_raise(FrozenError) { v2.map!{|e| e + 2 }}
+    assert_raise(FrozenError){ v3.map!{} }
+  end
+
+  def test_freeze
+    v = Vector[1,2,3]
+    f = v.freeze
+    assert_equal true, f.frozen?
+    assert v.equal?(f)
+    assert v.equal?(f.freeze)
+    assert_raise(FrozenError){ v[1] = 56 }
+    assert_equal v.dup, v
+  end
+
+  def test_clone
+    a = Vector[4]
+    def a.foo
+      42
+    end
+
+    v = a.clone
+    v[0] = 2
+    assert_equal a, v * 2
+    assert_equal 42, v.foo
+
+    a.freeze
+    v = a.clone
+    assert v.frozen?
+    assert_equal 42, v.foo
+  end
+
+  def test_dup
+    a = Vector[4]
+    def a.foo
+      42
+    end
+    a.freeze
+
+    v = a.dup
+    v[0] = 2
+    assert_equal a, v * 2
+    assert !v.respond_to?(:foo)
+  end
+
   def test_identity
     assert_same @v1, @v1
     assert_not_same @v1, @v2
Index: test/matrix/test_matrix.rb
===================================================================
--- test/matrix/test_matrix.rb	(revision 65506)
+++ test/matrix/test_matrix.rb	(revision 65507)
@@ -283,7 +283,18 @@ class TestMatrix < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/matrix/test_matrix.rb#L283
   end
 
   def test_collect
-    assert_equal(Matrix[[1, 4, 9], [16, 25, 36]], @m1.collect {|x| x ** 2 })
+    m1 = Matrix.zero(2,2)
+    m2 = Matrix.build(3,4){|row, col| 1}
+
+    assert_equal(Matrix[[5, 5, 5, 5], [5, 5, 5, 5], [5, 5, 5, 5]], m2.collect{|e| e * 5})
+    assert_equal(Matrix[[7, 0],[0, 7]], m1.collect(:diagonal){|e| e + 7})
+    assert_equal(Matrix[[0, 5],[5, 0]], m1.collect(:off_diagonal){|e| e + 5})
+    assert_equal(Matrix[[8, 1, 1, 1], [8, 8, 1, 1], [8, 8, 8, 1]], m2.collect(:lower){|e| e + 7})
+    assert_equal(Matrix[[1, 1, 1, 1], [-11, 1, 1, 1], [-11, -11, 1, 1]], m2.collect(:strict_lower){|e| e - 12})
+    assert_equal(Matrix[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], m2.collect(:strict_upper){|e| e ** 2})
+    assert_equal(Matrix[[-1, -1, -1, -1], [1, -1, -1, -1], [1, 1, -1, -1]], m2.collect(:upper){|e| -e})
+    assert_raise(ArgumentError) {m1.collect(:test){|e| e + 7}}
+    assert_not_equal(m2, m2.collect {|e| e * 2 })
   end
 
   def test_minor
@@ -623,6 +634,134 @@ class TestMatrix < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/matrix/test_matrix.rb#L634
     assert_equal Matrix.empty(0,3), Matrix.combine(Matrix.empty(0,3), Matrix.empty(0,3)) { raise }
   end
 
+  def test_set_element
+    src = Matrix[
+      [1, 2, 3, 4],
+      [5, 6, 7, 8],
+      [9, 10, 11, 12],
+    ]
+    rows = {
+      range:   [1..2, 1...3, 1..-1, -2..2, 1.., 1..., -2.., -2...],
+      int:     [2, -1],
+      invalid: [-4, 4, -4..2, 2..-4, 0...0, 2..0, -4..],
+    }
+    columns = {
+      range:   [2..3, 2...4, 2..-1, -2..3, 2.., 2..., -2..., -2..],
+      int:     [3, -1],
+      invalid: [-5, 5, -5..2, 2..-5, 0...0, -5..],
+    }
+    values = {
+      element: 42,
+      matrix:  Matrix[[20, 21], [22, 23]],
+      vector:  Vector[30, 31],
+      row:     Matrix[[60, 61]],
+      column:  Matrix[[50], [51]],
+      mismatched_matrix: Matrix.identity(3),
+      mismatched_vector: Vector[0, 1, 2, 3],
+    }
+    solutions = {
+      [:int, :int] => {
+        element: Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 42]],
+      },
+      [:range , :int] => {
+        element: Matrix[[1, 2, 3, 4], [5, 6, 7, 42], [9, 10, 11, 42]],
+        column:  Matrix[[1, 2, 3, 4], [5, 6, 7, 50], [9, 10, 11, 51]],
+        vector:  Matrix[[1, 2, 3, 4], [5, 6, 7, 30], [9, 10, 11, 31]],
+      },
+      [:int, :range] => {
+        element: Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 42, 42]],
+        row:     Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 60, 61]],
+        vector:  Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 30, 31]],
+      },
+      [:range , :range] => {
+        element: Matrix[[1, 2, 3, 4], [5, 6, 42, 42], [9, 10, 42, 42]],
+        matrix:  Matrix[[1, 2, 3, 4], [5, 6, 20, 21], [9, 10, 22, 23]],
+      },
+    }
+    solutions.default = Hash.new(IndexError)
+
+    rows.each do |row_style, row_arguments|
+      row_arguments.each do |row_argument|
+        columns.each do |column_style, column_arguments|
+          column_arguments.each do |column_argument|
+            values.each do |value_type, value|
+              expected = solutions[[row_style, column_style]][value_type] || Matrix::ErrDimensionMismatch
+
+              result = src.clone
+              begin
+                result[row_argument, column_argument] = value
+                assert_equal expected, result,
+                  "m[#{row_argument.inspect}][#{column_argument.inspect}] = #{value.inspect} failed"
+              rescue Exception => e
+                raise if e.class != expected
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+
+  def test_map!
+    m1 = Matrix.zero(2,2)
+    m2 = Matrix.build(3,4){|row, col| 1}
+    m3 = Matrix.zero(3,5).freeze
+    m4 = Matrix.empty.freeze
+
+    assert_equal Matrix[[5, 5, 5, 5], [5, 5, 5, 5], [5, 5, 5, 5]], m2.map!{|e| e * 5}
+    assert_equal Matrix[[7, 0],[0, 7]], m1.map!(:diagonal){|e| e + 7}
+    assert_equal Matrix[[7, 5],[5, 7]], m1.map!(:off_diagonal){|e| e + 5}
+    assert_equal Matrix[[12, 5, 5, 5], [12, 12, 5, 5], [12, 12, 12, 5]], m2.map!(:lower){|e| e + 7}
+    assert_equal Matrix[[12, 5, 5, 5], [0, 12, 5, 5], [0, 0, 12, 5]], m2.map!(:strict_lower){|e| e - 12}
+    assert_equal Matrix[[12, 25, 25, 25], [0, 12, 25, 25], [0, 0, 12, 25]], m2.map!(:strict_upper){|e| e ** 2}
+    assert_equal Matrix[[-12, -25, -25, -25], [0, -12, -25, -25], [0, 0, -12, -25]], m2.map!(:upper){|e| -e}
+    assert_equal m1, m1.map!{|e| e ** 2 }
+    assert_equal m2, m2.map!(:lower){ |e| e - 3 }
+    assert_raise(ArgumentError) {m1.map!(:test){|e| e + 7}}
+    assert_raise(FrozenError) { m3.map!{|e| e * 2} }
+    assert_raise(FrozenError) { m4.map!{} }
+  end
+
+  def test_freeze
+    m = Matrix[[1, 2, 3],[4, 5, 6]]
+    f = m.freeze
+    assert_equal true, f.frozen?
+    assert m.equal?(f)
+    assert m.equal?(f.freeze)
+    assert_raise(FrozenError){ m[0, 1] = 56 }
+    assert_equal m.dup, m
+  end
+
+  def test_clone
+    a = Matrix[[4]]
+    def a.foo
+      42
+    end
+
+    m = a.clone
+    m[0, 0] = 2
+    assert_equal a, m * 2
+    assert_equal 42, m.foo
+
+    a.freeze
+    m = a.clone
+    assert m.frozen?
+    assert_equal 42, m.foo
+  end
+
+  def test_dup
+    a = Matrix[[4]]
+    def a.foo
+      42
+    end
+    a.freeze
+
+    m = a.dup
+    m[0, 0] = 2
+    assert_equal a, m * 2
+    assert !m.respond_to?(:foo)
+  end
+
   def test_eigenvalues_and_eigenvectors_symmetric
     m = Matrix[
       [8, 1],

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]