mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
lib/matrix.rb: Make Matrix & Vector mutable. Add #[]=, #map!.
Adapted from patch by Grzegorz Jakubiak. [#14151] [Fix GH-1769] [Fix GH-1905] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65507 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
b9d42af0f2
commit
eda970cfe2
4 changed files with 476 additions and 22 deletions
8
NEWS
8
NEWS
|
@ -313,6 +313,14 @@ sufficient information, see the ChangeLog file or Redmine
|
|||
|
||||
* Matrix#antisymmetric? / #skew_symmetric?
|
||||
|
||||
* Matrix#map! / #collect! [Feature #14151]
|
||||
|
||||
* Matrix#[]=
|
||||
|
||||
* Vector#map! / #collect!
|
||||
|
||||
* Vector#[]=
|
||||
|
||||
[Net]
|
||||
|
||||
[New options]
|
||||
|
|
247
lib/matrix.rb
247
lib/matrix.rb
|
@ -302,12 +302,107 @@ class Matrix
|
|||
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,17 +455,49 @@ class Matrix
|
|||
#
|
||||
# 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.
|
||||
|
@ -865,12 +992,11 @@ class Matrix
|
|||
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
|
|||
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
|
|||
# To access elements:
|
||||
# * #[](i)
|
||||
#
|
||||
# To set elements:
|
||||
# * #[]=(i, v)
|
||||
#
|
||||
# To enumerate the elements:
|
||||
# * #each2(v)
|
||||
# * #collect2(v)
|
||||
|
@ -1678,8 +1827,10 @@ end
|
|||
# * #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
|
|||
# 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
|
|||
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
|
|||
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 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||
#++
|
||||
|
@ -1885,13 +2086,6 @@ class Vector
|
|||
@elements.eql? other.elements
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a copy of the vector.
|
||||
#
|
||||
def clone
|
||||
self.class.elements(@elements)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a hash-code for the vector.
|
||||
#
|
||||
|
@ -2042,6 +2236,17 @@ class Vector
|
|||
end
|
||||
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
|
||||
|
|
|
@ -283,7 +283,18 @@ class TestMatrix < Test::Unit::TestCase
|
|||
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
|
|||
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],
|
||||
|
|
|
@ -27,6 +27,108 @@ class TestVector < Test::Unit::TestCase
|
|||
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
|
||||
|
|
Loading…
Reference in a new issue