I've been trying to code some algorithm in a minified manner>.
I believe that typical 2d array defining is looks too unwieldy
mx = Array.new(N) { Array.new(N) }
I am trying to use another hint, but it's behaviour is a bit strange for me:
mx = [[!:&]*n]*n
Take a look:
#square matrix n*n
n=3
mx = [[!:&]*n]*n
#=> [[false, false, false], [false, false, false], [false, false, false]]
mx[0][0]=true
mx
#=> [[true, false, false], [true, false, false], [true, false, false]]
# true true true ????
#.
How should I tame my array?
repl.it_snippet: avaiable
This is because the sub arrays are the same instance
#square matrix n*n
n=3
mx = [[!:&]*n]*n
#=> [[false, false, false], [false, false, false], [false, false, false]]
mx[0] === mx[1]
# true
mx[1] === mx[2]
# true
to fix the code, add something like that
n=3
mx = [[!:&]*n]*n
mx = mx.flatten.each_slice(n).to_a
mx[0][0] = true
mx
# [[true, false, false], [false, false, false], [false, false, false]]
or
n=3
mx = ([!:&]*n*n).each_slice(n).to_a
mx[0][0] = true
mx
# [[true, false, false], [false, false, false], [false, false, false]]
Is sort in Ruby stable? That is, for elements that are in a tie for sort, is the relative order among them preserved from the original order? For example, given:
a = [
{id: :a, int: 3},
{id: :b, int: 1},
{id: :c, int: 2},
{id: :d, int: 0},
{id: :e, int: 1},
{id: :f, int: 0},
{id: :g, int: 1},
{id: :h, int: 2},
]
is it guaranteed that we always get for
a.sort_by{|h| h[:int]}
the following
[
{id: :d, int: 0},
{id: :f, int: 0},
{id: :b, int: 1},
{id: :e, int: 1},
{id: :g, int: 1},
{id: :c, int: 2},
{id: :h, int: 2},
{id: :a, int: 3},
]
without any variation for the relative order among the elements with the :id value :d, :f, and among :b, :e, :g, and among :c, :h? If that is the case, where in the documentation is it described?
This question may or may not have connection with this question.
Both MRI's sort and sort_by are unstable. Some time ago there was a request to make them stable, but it was rejected. The reason: Ruby uses an in-place quicksort algorithm, which performs better if stability is not required. Note that you can still implement stable methods from unstable ones:
module Enumerable
def stable_sort
sort_by.with_index { |x, idx| [x, idx] }
end
def stable_sort_by
sort_by.with_index { |x, idx| [yield(x), idx] }
end
end
No, ruby's built-in sort is not stable.
If you want stable sort, this should work. You probably want to create a method for it if you're going to use it often.
a.each_with_index.sort_by {|h, idx| [h[:int], idx] }.map(&:first)
Basically it keeps track of the original array index of each item, and uses it as a tie-breaker when h[:int] is the same.
More info, for the curious:
As far as I know, using the original array index as the tie-breaker is the only way to guarantee stability when using an unstable sort. The actual attributes (or other data) of the items will not tell you their original order.
Your example is somewhat contrived because the :id keys are sorted ascending in the original array. Suppose the original array were sorted descending by :id; you'd want the :id's in the result to be descending when tie-breaking, like so:
[
{:id=>:f, :int=>0},
{:id=>:d, :int=>0},
{:id=>:g, :int=>1},
{:id=>:e, :int=>1},
{:id=>:b, :int=>1},
{:id=>:h, :int=>2},
{:id=>:c, :int=>2},
{:id=>:a, :int=>3}
]
Using the original index will handle this too.
Update:
Matz's own suggestion (see this page) is similar, and may be slightly more efficient than the above:
n = 0
ary.sort_by {|x| n+= 1; [x, n]}
For some implementations of Ruby, sort is stable, but you shouldn't depend upon it. Stability of Ruby's sort is implementation defined.
What the documentation says
The documentation says that you should not depend upon sort being stable:
The result is not guaranteed to be stable. When the comparison of two elements returns 0, the order of the elements is unpredictable.
Note that this does not say whether or not the sort is stable. It just says it is not guaranteed to be stable. Any given implementation of Ruby could have a stable sort and still be consistent with the documentation. It could also have an unstable sort, or change whether the sort is stable at any time.
What Ruby actually does
This test code prints true if Ruby's sort is stable, or false if it is not:
Foo = Struct.new(:value, :original_order) do
def <=>(foo)
value <=> foo.value
end
end
size = 1000
unsorted = size.times.map do |original_order|
value = rand(size / 10)
Foo.new(value, original_order)
end
sorted = unsorted.sort
stably_sorted = unsorted.sort_by do |foo|
[foo.value, foo.original_order]
end
p [RUBY_PLATFORM, RUBY_VERSION, RUBY_PATCHLEVEL, sorted == stably_sorted]
Here are the results for all of the Rubies I have installed on my Linux box:
["java", "1.8.7", 357, false]
["java", "1.9.3", 551, false]
["java", "2.3.3", 0, true]
["java", "2.5.7", 0, true]
["x86_64-linux", "1.8.7", 374, false]
["x86_64-linux", "1.8.7", 374, false]
["x86_64-linux", "1.8.7", 376, false]
["x86_64-linux", "1.9.3", 392, false]
["x86_64-linux", "1.9.3", 484, false]
["x86_64-linux", "1.9.3", 551, false]
["x86_64-linux", "2.0.0", 643, false]
["x86_64-linux", "2.0.0", 648, false]
["x86_64-linux", "2.1.0", 0, false]
["x86_64-linux", "2.1.10", 492, false]
["x86_64-linux", "2.1.1", 76, false]
["x86_64-linux", "2.1.2", 95, false]
["x86_64-linux", "2.1.3", 242, false]
["x86_64-linux", "2.1.4", 265, false]
["x86_64-linux", "2.1.5", 273, false]
["x86_64-linux", "2.1.6", 336, false]
["x86_64-linux", "2.1.7", 400, false]
["x86_64-linux", "2.1.8", 440, false]
["x86_64-linux", "2.1.9", 490, false]
["x86_64-linux", "2.2.0", 0, true]
["x86_64-linux", "2.2.1", 85, true]
["x86_64-linux", "2.2.2", 95, true]
["x86_64-linux", "2.2.3", 173, true]
["x86_64-linux", "2.2.4", 230, true]
["x86_64-linux", "2.2.5", 319, true]
["x86_64-linux", "2.2.6", 396, true]
["x86_64-linux", "2.3.0", 0, true]
["x86_64-linux", "2.3.1", 112, true]
["x86_64-linux", "2.3.2", 217, true]
["x86_64-linux", "2.3.3", 222, true]
["x86_64-linux", "2.4.0", 0, true]
["x86_64-linux", "2.4.0", -1, true]
["x86_64-linux", "2.4.0", -1, true]
["x86_64-linux", "2.4.0", -1, true]
["x86_64-linux", "2.4.0", -1, true]
["x86_64-linux", "2.4.1", 111, true]
["x86_64-linux", "2.4.2", 198, true]
["x86_64-linux", "2.4.5", 335, true]
["x86_64-linux", "2.4.9", 362, true]
["x86_64-linux", "2.5.0", 0, true]
["x86_64-linux", "2.5.3", 105, true]
["x86_64-linux", "2.5.7", 206, true]
["x86_64-linux", "2.6.0", 0, true]
["x86_64-linux", "2.6.2", 47, true]
["x86_64-linux", "2.6.3", 62, true]
["x86_64-linux", "2.6.4", 104, true]
["x86_64-linux", "2.6.5", 114, true]
["x86_64-linux", "2.6.6", 146, true]
["x86_64-linux", "2.7.0", 0, true]
["x86_64-linux", "2.7.1", 83, true]
["x86_64-linux", "2.7.2", 137, true]
["x86_64-linux", "3.0.0", 0, true]
["x86_64-linux", "3.0.0", -1, true]
["x86_64-linux", "3.0.1", 64, true]
["x86_64-linux", "3.0.2", 107, true]
["x86_64-linux", "3.0.3", 157, true]
["x86_64-linux", "3.1.0", 0, true]
["x86_64-linux", "3.1.1", 18, true]
["x86_64-linux", "3.1.2", 20, true]
We can see that JRuby is unstable, and MRI before 2.2, on Linux, is unstable. MRI >= 2.2.0 is stable (again, on Linux).
The platform matters, though. Although the above result shows that sort is stable in MRI 2.4.1 on Linux, the same version is unstable on Windows:
["x64-mingw32", "2.4.1", 111, false]
Why is MRI's sort stable on Linux, but not on Windows?
Even within a single version of a Ruby implementation, the sort algorithm can change. MRI can use at least three different sorts. The sort routine is selected at compile time using a series of #ifdefs in util.c. It looks like MRI has the ability to use sorts from at least two different libraries. It also has its own implementation.
What should you do about it?
Since the sort may be stable but can't be guaranteed to be stable, do not write code which depends upon Ruby's sort being stable. That code could break when used on a different version, implementation, or platform.
personally I wouldn't count on this. How bout doing something like this:
a.sort {|a, b| s1 = a[:int] <=> b[:int]; s1 != 0 ? s1 : a[:id] <=> b[:id] }
I have a situation in which I want to separate an array initialized with nils into the empty segments and segments containing consecutive runs of numbers.
I found the higher order chunk function of Array to provide an elegant solution to this:
<< [nil,nil,1,2,nil,3].chunk { |e| !e.nil? }.each { |e| p e }
>> [false, [nil, nil]]
>> [true, [1, 2]]
>> [false, [nil]]
>> [true, [3]]
However, suppose that I would like the output to also include the starting index into the original array in each chunk, i.e. augmenting the output above to something like:
>> [false, 0, [nil, nil]]
>> [true, 2, [1, 2]]
>> [false, 4, [nil]]
>> [true, 5, [3]]
Is there a solution to obtaining this that retains the expressiveness of the chunk snippet in the above?
Thanks in advance.
A functional modular approach: index the input array, chunk, and map it to the desired output, O(n):
data = xs.each_with_index.chunk { |x, i| !x.nil? }.map do |match, pairs|
[match, pairs.first[1], pairs.map(&:first)]
end
#=> [[false, 0, [nil, nil]], [true, 2, [1, 2]], [false, 4, [nil]], [true, 5, [3]]]
You can just walk through the output of chunk and track starting index yourself. Simply increment it by length of each chunk. Here's my quick attempt.
chunked = [nil,nil,1,2,nil,3].chunk { |e| !e.nil? }
_, chunks_with_embedded_index = chunked.each_with_object([0, []]) do |(k, chunk), memo|
memo[1] << [k, memo[0], chunk]
memo[0] += chunk.length
end
chunks_with_embedded_index.each {|c| p c }
# >> [false, 0, [nil, nil]]
# >> [true, 2, [1, 2]]
# >> [false, 4, [nil]]
# >> [true, 5, [3]]
You can extract the temp var for last index into a full local variable and have somewhat cleaner implementation:
chunked = [nil,nil,1,2,nil,3].chunk { |e| !e.nil? }
last_index = 0
chunks_with_embedded_index = chunked.reduce([]) do |memo, (k, chunk)|
memo << [k, last_index, chunk]
last_index += chunk.length
memo
end
chunks_with_embedded_index.each {|c| p c }
# >> [false, 0, [nil, nil]]
# >> [true, 2, [1, 2]]
# >> [false, 4, [nil]]
# >> [true, 5, [3]]
( Part of ) a matrix representation of the objects I work with is the following:
{
{1, A,{100, 20, 30},10},
{2, B,{100}, 0},
{3, X,{120,20},0},
{4, C,{},11}
}
I want to store this data externally in XML Format as follows
<data>
<row key="1" val1="A" val2="10"> <occ>100</occ><occ>20</occ><occ>30</occ></row>
<row key="2" val1="B" val2="0"><occ>100</occ></row>
<row key="3" val1="X" val2="0"><occ>120</occ><occ>20</occ></row>
<row key="4" val1="C" val2="11"></row>
</data>
I am looking for an example on how to:
- transfer the the matrix to XML ( which Mathematica commands ? )
- parse the XML string back to matrix format after having imported the XML file.
Here we import your data as symbolic XML:
In[50]:= xml = Import["C:\\Temp\\matrixData.xml"]
Out[50]= XMLObject["Document"][{},
XMLElement["data", {},
{XMLElement["row", {"key" -> "1", "val1" -> "A", "val2" -> "10"},
{XMLElement["occ", {}, {"100"}], XMLElement["occ", {}, {"20"}],
XMLElement["occ", {}, {"30"}]}],
XMLElement["row", {"key" -> "2", "val1" -> "B", "val2" -> "0"},
{XMLElement["occ", {}, {"100"}]}],
XMLElement[ "row", {"key" -> "3", "val1" -> "X","val2" -> "0"},
{XMLElement["occ", {}, {"120"}], XMLElement["occ", {}, {"20"}]}],
XMLElement["row", {"key" -> "4", "val1" -> "C", "val2" -> "11"}, {}]}], {}]
Here we parse into a matrix:
In[51]:= matr =
xml /. XMLObject["Document"][{}, data_, _] :> data /.
XMLElement["data", _, children_] :> children /.
XMLElement["row", attrs_, vals_] :> {"key" /. attrs,
"val1" /. attrs, vals /. XMLElement["occ", _, {val_}] :> val,
"val2" /. attrs} /. s_String :> ToExpression[s]
Out[51]= {{1, A, {100, 20, 30}, 10}, {2, B, {100}, 0}, {3,X, {120, 20}, 0}, {4, C, {}, 11}}
The code is short and economical due to the use of rule-within-a-rule technique. I discuss it here. One nice application of this technique can be found here.
Here is the reverse:
XMLObject["Document"][{}, XMLElement["data", {},
Replace[matr, {key_, val1_, vals_List, val2_} :>
XMLElement[ "row", {"key" -> ToString[key], "val1" -> ToString[val1],
"val2" -> ToString[val2]},
XMLElement["occ", {}, {ToString[#]}] & /# vals], {1}]], {}]
I do not show the result but it is the same as the imported symbolic XML. You can call Export with this symbolic XML and it will know how to handle it.