No implicit conversion of Struct into Array - arrays

I had an occurrence where I was working with an array of structs and wanted to transpose them, though was met with an error:
TypeError (no implicit conversion of Struct into Array)
I'd thought (incorrectly) that such implicit conversion simply called to_a on each object if available, though structs do have a to_a method.
So my question is two part:
if I can, how do I implement this implicit conversion?
secondly, why is this the case? Why can't structs be implicitly converted to arrays, yet can be explicitly converted?
Here's a minimal example to produce the error:
S = Struct.new(:a, :b)
a = S.new(1, 2)
# => #<struct S a=1, b=2>
b = S.new(3, 4)
# => #<struct S a=3, b=4>
[a, b].transpose
# TypeError (no implicit conversion of S into Array)
[a, b].map(&:to_a)
# => [[1, 2], [3, 4]]
# Therefore, the extra step I'd have to take to transpose:
[a, b].map(&:to_a).transpose
# => [[1, 3], [2, 4]]
Thanks in advance for any help.

I've actually found the answer to this while researching the question, so will pop the answer in as I couldn't find anything similar when searching earlier.
Ruby uses different coercion methods for explicit vs implicit conversion:
| Explicit | Implicit |
|----------|----------|
| to_i | to_int |
| to_s | to_str |
| to_a | to_ary |
| to_h | to_hash |
|----------|----------|
So the problem here is structs don't have a to_ary method:
a.to_ary
# NoMethodError (undefined method `to_ary' for #<struct S a=1, b=2>)
Therefore, if we define this method on the struct in question, we can implicitly convert:
S.define_method(:to_ary) do
self.to_a
end
[a, b].transpose
# => [[1, 3], [2, 4]]
Voila :)

if I can, how do I implement this implicit conversion?
You can pass a block to Struct.new and then define your method there:
S = Struct.new(:a, :b) do
alias_method :to_ary, :to_a
end
secondly, why is this the case? Why can't structs be implicitly converted to arrays, yet can be explicitly converted?
This is somewhat philosophical, but to_ary, to_int, etc. indicate that those objects really represent an array, integer, etc. respectively; they're just not exactly the right type.
It's different for the one letter variants of to_a, to_i, which indicate that the object could fall in for an array, integer, etc. but their underlying structure is quite different, or only represents a portion of their true semantics.

Related

Destructuring assignment in object creation

As with my previous question, this is an area where I can't tell if I've encountered a bug or a hole in my understanding of Raku's semantics. Last time it turned out to be a bug, but doubt lightning will strike twice!
In general, I know that I can pass named arguments to a function either with syntax that looks a lot like creating a Pair (e.g. f :a(42)) or with syntax that looks a lot like flattening a Hash (e.g., f |%h). (see argument destructuring in the docs). Typically, these two are equivalent, even for non-Scalar parameters:
sub f(:#a) { dd #a }
my %h = a => [4, 2];
f :a([4,2]); # OUTPUT: «Array element = [4, 2]»
f |%h; # OUTPUT: «Array element = [4, 2]»
However, when constructing an object with the default .new constructor, these two forms seem to give different results:
class C { has #.a; }
my %h = a => [4, 2];
C.new: :a([4,2]; # OUTPUT: «C.new(a => ([[4, 2]])»
C.new: |%h; # OUTPUT: «C.new(a => [[4, 2],])»
That is, passing :a([4,2]) results in a two-element Array, but using the argument-flattening syntax results in a one-element Array containing a two-element Array.
Is this behavior intended? If so, why? And is there syntax I can use to pass |%h in and get the two-element Array bound to an #-sigiled attribute? (I know using an $-sigiled attribute works, but I prefer the semantics of the #).
Is this behavior intended?
Yes. Parameter binding uses binding semantics, while attribute initialization uses assignment semantics. Assignment into an array respects Scalar containers, and the values of a Hash are Scalar containers.
If so, why?
The intuition is:
When calling a function, we're not going to be doing anything until it returns, so we can effectively lend the very same objects we pass to it while it executes. Thus binding is a sensible default (however, one can use is copy on a parameter to get assignment semantics).
When creating a new object, it is likely going to live well beyond the constructor call. Thus copying - that is, assignment - semantics are a sensible default.
And is there syntax I can use to pass |%h in and get the two-element Array bound to an #-sigiled attribute?
Coerce it into a Map:
class C { has #.a; }
my %h = a => [4, 2];
say C.new: |%h.Map;
Or start out with a Map in the first place:
class C { has #.a; }
my %h is Map = a => [4, 2];
say C.new: |%h;

How do I change the functionality of the built in Array class in Ruby?

I am writing code in Ruby.
Say I have an array like this:
myarray = [1,2,3,4,5]
I want to change the code of the Array class so that myarray[7] would return "out of bounds" instead of nil.
I don't want to make so that I check if something is out of bounds and then return "out of bounds", I want to implement this in Array directly.
Is this possible? Do I have to extend the Array class?
I'm Not sure how to start, very new to ruby. My initial thoughts were overriding a method in the Array class, but I'm not sure how to do that when I don't know what method is being used in myarray[i].
Suppose
arr = [1,2,3,4,5]
Let's start by examining the doc Array#[]. As seen, that method can have a single integer argument:
arr[2] #=> 3
arr[-2] #=> 4
arr[5] #=> nil
arr[-7] #=> nil
arr['x'] #=> TypeError (no implicit conversion of String into Integer)
or it may have two integer arguments:
arr[1,3] #=> [2, 3, 4]
arr[-3,2] #=> [3, 4]
arr[3,5] #=> [4, 5]
arr[7,4] #=> nil
arr[2,'x'] #=> TypeError (no implicit conversion of String into Integer)
or it may have a range of integers as an argument:
arr[1..3] #=> [2, 3, 4]
arr[1...3] #=> [2, 3]
arr[-5..-2] #=> [1, 2, 3, 4]
arr[3..9] #=> [4, 5]
arr[7..10] #=> nil
arr[1..'x'] #=> ArgumentError (bad value for range)
As I understand, you wish to alter Array#[] in such way that its behavior is unchanged except for the case when its argument is a single integer representing an offset into the array that is out-of-bounds.1. That could be done by writing the method in a module and then prepend that module to the class Array with the method Module#prepend.
module NewSlice
def [](*args)
arg1, arg2 = args
if arg2.nil? && arg1.is_a?(Integer) &&
(arg1 > self.size-1 || arg1 < -self.size)
"out of range"
else
super
end
end
end
Array.prepend(NewSlice)
arr[2] #=> 3
arr[-2] #=> 4
arr[5] #=> "out of range"
arr[-7] #=> "out of range"
arr['x'] #=> TypeError (no implicit conversion of String into Integer)
arr[1,3] #=> [2, 3, 4]
arr[-3,2] #=> [3, 4]
arr[3,5] #=> [4, 5]
arr[7,4] #=> nil
arr[2,'x'] #=> TypeError (no implicit conversion of String into Integer)
arr[1..3] #=> [2, 3, 4]
arr[1...3] #=> [2, 3]
arr[-5..-2] #=> [1, 2, 3, 4]
arr[3..9] #=> [4, 5]
arr[7..10] #=> nil
arr[1..'x'] #=> ArgumentError (bad value for range)
We see that
Array.ancestors
#=> [NewSlice, Array, Enumerable, Object, Kernel, BasicObject]
showing that super in NewSlice#[] calls the original method Array#[] after NewSlice has been prepended to Array. Since super has no arguments all the arguments that were passed to the new Array#[] method ( defined in NewSlice) are passed to the original Array#[].
Aliases were commonly used in Ruby prior to the introduction of Module#prepend in Ruby v2.0. They are still used but their importance has diminished greatly since prepend has become available.
If nil is to be replaced by "out of bounds" in all cases where Array#[] returns nil, write
module NewSlice
def [](*args)
super || "out of bounds"
end
end
Array.prepend(NewSlice)
arr[5] #=> "out of range"
arr[-7] #=> "out of range"
arr[7,4] #=> "out of range"
arr[7..10] #=> "out of range"
1. Whether it is advisable to modify core methods such as Array#[] is a separate question, but it can result in pain and suffering.
TL;DR
This is an interesting question about how to monkeypatch core Ruby classes like Array. However, I'd be remiss if I didn't point out that it involves some hijinks that seem unnecessary, since you could get the same behavior with a simple logical OR operator like:
[1, 2, 3][5] || 'out of bounds'
#=> "out of bounds"
Still, if you want to change the behavior of a core class then it can certainly be done.
Re-Open the Core Class
In Ruby, almost everything that isn't a keyword is a method. Pragmatically, Array::[] is really just a method for indexing into an array object, with a little syntactic sugar from the interpreter to allow its arguments to be placed inside the brackets. Because it's just an instance method defined by the Array class, it can be modified in various ways such as re-opening the core class to modify the method, or by creating a singleton method on the instance. The first is conceptually simpler, but singleton methods are probably the safest solution for real-world programming.
You can re-open the Array class and redefine Array::[] as follows:
class Array
# save off a copy of the existing method; we still need it
alias_method :'old_[]', :'[]' unless defined? Array::old_[]
# pass all arguments to the old method, but return a string
# when the result is falsey
def [] *args
send("old_[]", *args) || 'out of bounds'
end
end
You can then demonstrate that it works like a regular Array, but with your expected behavior:
a = [10, 20, 30]
a[0] #=> 10
a[0,2] #=> [10, 20]
a[100] #=> "out of bounds"
Add a Singleton Method
While the code above works, it's just generally a bad idea to monkeypatch a core class like Array. If you don't want to encapsulate this behavior in a subclass (e.g. class MyArray < Array), then you should use refinements (using Module#refine) or singleton methods like the following to reduce the scope of your changes. For example:
a = [10, 20, 30]
class << a
alias_method :'old_[]', :'[]' unless self.respond_to? 'old_[]'
def [] *args
send("old_[]", *args) || 'out of bounds'
end
end
By limiting the scope of our change to a single instance, we're much less likely to cause serious or unrecoverable problems with core classes. A lot of things can go wrong in a Ruby program if a core class like Array starts returning truthy values (e.g. a String value such as out of bounds) rather than a falsey value like nil, so caveat emptor!
If you really want to break existing code (standard classes and ruin even methods of the current Array class itself, which rely on the current behaviour), you can of course monkeypatch it - open it and redefine the method in any way you like, as has been outlined in various answers to this question.
A more sane approach would be to subclass Array, i.e.
class BoundCheckingArray < Array
# redefined the methods according to your taste.
end
Of course this would violate the Liskov Substitution Principle.
Another approach will be to define a complete separate class and use Delegator to avoid rewriting everything from scratch, by delegating methods you did not rewrite explicitly, to the underlying array.
In both cases you need to be aware, that those methods in your BoundCheckingArray, which you did not explicitly rewrite and hence are taken from BoundCheckingArray, will stop working, if they assume that they can safely access the array outside of the bounds.
Another possibility would be to not modify existing Array methods, but simply add bound checking setters and getters, i.e. with monkeypatching:
class Array
def at_checked(i)
fail "out_of_bounds" if i >= size || i < -size
at(i)
end
def set_checked(i, new_value)
# analoguous
end
end
Of course this approach would also work with subclassing (now adhering to Liskov's principle, because you just added methods) and delegating. Note that I coped explicitly with negative index values, assuming that you would like to use this feature in your bound checking array too.
You may know classes in Ruby are defined like:
class Foo
def foo
'foo'
end
end
Foo.new.foo # calls instance method `foo` on the instance of `Foo`
# => 'foo'
Classes and modules in Ruby can be "reopened" for re-definition (although it's a bit frowned upon -- makes it difficult to track code b/c code can be in either/any place):
class Foo
def bar
'bar'
end
def foo
'overwrite foo'
end
end
f = Foo.new
f.bar
# => 'bar'
f.foo
# => 'overwrite foo'
This means you can also do:
class Array
# re-define anything you want
end
# alternatively:
Array.class_eval do
# the same as the above, in most ways
end
Even though it's even less recommended to modify existing methods of core Ruby classes. Not only does it break assumptions other Ruby developers may have of the code (i.e. misleading code), its global effect will likely break behaviors in other libraries, etc.

Julia: Initializing numeric arrays of different types

I am trying to build a two element array in Julia, where each sub-array has a different type (one is a vector of Int64s, the other is an array of Float32s).
The code belows automatically converts the element that I want to be an Int64 into a Float32, which is what I don't want:
my_multitype_array = [ collect(1:5), rand(Float32,3) ]
The resulting array automatically converts the Int64s in the first array (defined via collect(1:5)) into a Float32, and the resulting my_multitype_array has type 2-element Array{Array{Float32,1}}. How do I force it to make the first sub-array remain Int64s? Do I need to perhaps pre-define my_multitype_array to be an empty array with two elements of the desired types, before filling it out with values?
And finally, once I do have the desired array with different types, how would I refer to it, when pre-stating its type in a function? See below for what I mean:
function foo_function(first_scalar_arg::Float32, multiple_array_arg::Array{Array{Float32,1}})
# do stuff
return
end
Instead of ::Array{Array{Float32,1}}, would I write ::Array{Array{Any,1}} or something?
I think that the following code matches better what was asked in the question:
julia> a = Union{Array{Int},Array{Float64}}[[1,2,3],rand(2,2)]
2-element Array{Union{Array{Float64,N} where N, Array{Int64,N} where N},1}:
[1, 2, 3]
[0.834902264215698 0.42258382777543124; 0.5856562680004389 0.6654033155981287]
This creates an actual data structure which knows that it contains either Float64 or Int arrays.
Some usage
julia> a[1]
3-element Array{Int64,1}:
1
2
3
julia> a[2]
2×2 Array{Float64,2}:
0.834902 0.422584
0.585656 0.665403
And manipulating the structure:
julia> push!(a, [1, 1]); #works
julia> push!(a, [true, false]);
ERROR: MethodError: no method matching Union{Array{Float64,N} where N, Array{Int64,N} where N}(::Array{Bool,1})
How to instantiate a vector of different types:
If you type the vector in a terminal, it will be promoted to the largest common type:
julia> [[1], [1.0]]
2-element Array{Array{Float64,1},1}:
[1.0]
[1.0]
The reason for that is that you don't specify the type of the outer vector, so Julia will try to infer the type based on the contents. More specific types are always more efficient, so if the vector types can be converted to a single type that can represent all the inner vectors, this will be done (through the promote mechanism). To avoid it, you need to manually specify the outer vector type e.g.:
julia> Any[[1], [1.0]]
2-element Array{Any,1}:
[1]
[1.0]
How to refer to vectors of differently-typed vectors
When you think about it, "vectors of differently-typed vectors" is not a single type, but an infinite set of types. These kind of types are called "unionall types" in Julia, and are represented by the where keyword. In this case, you want Vector{T} where T <: Vector.
But wait! Then how come:
julia> Any[[1], [1.0]] isa Vector{T} where T <: Vector
false
Well, a vector that can contain any element is not really a vector of vectors. So here you have two options:
Either relax your function signature by just removing the type annotations or relatixing them significantly (this is preferred because the value you pass in may actually be a vector of vectors even if its type is e.g. Vector{Any}):
function foo_function(first_scalar_arg, multiple_array_arg::AbstractArray)
# do stuff
return
end
Or else be vigilant that you make sure to construct a "vector of vectors" initially:
julia> Vector[[1], [1.0]]
2-element Array{Array{T,1} where T,1}:
[1]
[1.0]
julia> Vector[[1], [1.0]] isa Vector{T} where T <: Vector
true
To expand a little on #Przemyslaw Szufel's answer...
Creating vectors with elements of mixed types is tricky, as you've seen, since the literal array constructor attempts to promote the elements to a common type. There is a special syntax to get around that, which is described in the manual here.
In your case, you can construct your vector of vectors as follows:
julia> Union{Vector{Int64}, Vector{Float32}}[[1, 2], [1.0f0, 2.0f0]]
2-element Array{Union{Array{Float32,1}, Array{Int64,1}},1}:
[1, 2]
Float32[1.0, 2.0]
The prefix to the literal array constructor specifies the element type of the array. So in this case, the element type of the vector is constrained to be
Union{Vector{Int64}, Vector{Float32}}
In other words, the elements of the outer vector must be either vectors of Int64 or vectors of Float32.

Infinitely expanding array

How would I go about creating an array of arrays, that can continue that way, adding arrays inside arrays etc, without explicitly knowing how many arrays can contain arrays?
On top of this, out of curiosity, is it possible to change type in place with Arrays, for example if I create an array with ["test"] can I subsequently change it to [["test"]] and so on?
Any comprehensive tutorials on how arrays can be nested etc would be amazing, but currently it's still very difficult to search for crystal topics.
You can use recursive aliases for this (see language reference for alias):
alias NestedArray = Array(NestedArray) | <YourArrayItemType(s)>
An example (carc.in):
alias NestedArray = Array(NestedArray) | Int32
array = [] of NestedArray
array << 1
array << [2, 3, 4, [5, [6, 7, [8] of NestedArray] of NestedArray] of NestedArray] of NestedArray
array << Array(NestedArray){Array(NestedArray){10, 11}}
array # => [1, [2, 3, 4, [5, [6, 7, [8]]]], [[10, 11]]]
Concerning the second question, I am not sure what you mean. You can change the type of a variable like this:
array = ["test"]
array = [array]
array # => [["test"]]

Groovy each method on list of arrays

I met quite strange behaviour of each method on list of arrays in groovy.
I have given piece of code.
def list = [
[2, "foo"].toArray(),
[4, "bar"].toArray()
]
list.each { def row ->
println(row.length)
}
Which gives me pretty expecting result in console
2
2
Then I did small modification to this code
def list = [
[2, "foo"].toArray(),
[4, "bar"].toArray()
]
list.each { Object[] row ->
println(row.length)
}
And result is
1
1
Because variable row is array with one element which is my original 2 elements array from list.
Is there some explanation for this?
Im using groovy 1.8.8 or 2.1.2
I guess it's a feature rather than a bug :p
Object[] in a closure declaration has a special semantic for variable argument:
From http://groovy.codehaus.org/Closures+-+Formal+Definition:
Groovy has special support for excess arguments. A closure may be declared with its last argument of type Object[]. If the developer does this, any excess arguments at invocation time are placed in this array. This can be used as a form of support for variable numbers of arguments.
In your example, the argument passed to the closure will be wrapped again with a new Object array, containing list as the only element.
As an example:
def list = [
[2, "foo"].toArray(),
[4, "bar"].toArray()
]
def c = {Object[] args -> println args}
c(list)
Output:
[[[2, foo], [4, bar]]]

Resources