I have an array of S-vectors and I want to do something like:
b .%5
and perform the mod
across all elements of each vector. So I built this:
function Base.rem(x::SVector{Float64},y::Float64)::SVector{Float64}
SVector(x[1]%y,x[2]%y)
end
and it didn’t work. It wasn’t until I modified it to be like:
function Base.rem(x::SArray{Tuple{2},Float64,1,2},y::Float64)::SArray{Tuple{2},Float64,1,2}
SVector(x[1]%y,x[2]%y)
end
that it worked for me. So I guess my question is exposing my lack of understanding of types. When I declare a variable to be an SVector, I do it like this:
vec:: SVector{2,Float64}
so why is the type different for the function definition I tried above? The issue is more general than just “How do I mod over an array of SVectors?” because I got it working. I think I just need help understanding types in general. (Yes, I’ll re-read the documentation on that too.)
Any thoughts or ideas to share?
Secondary question: Could I define rem
so that it would take an SVector of any length?
That’s not what what the SVector
type signature looks like — it is parameterized by two numbers, the length of the vector and the type of the elements. Since your code only works for an SVector
with two elements, you could do: x::SVector{2,Float64}
, for example.
But you seem to be making it much more complicated than necessary. Why not just do:
myrem(x::SVector, y::Number) = x .% y
which will do the elementwise %
that you want for any number of elements and any types? (I wouldn’t advise overloading Base.rem
, since that’s type piracy.)
(Realize that argument-type declarations and return-type declarations do nothing for performance here. Even with no argument-type declarations at all, Julia will compile a specialized version of the function for any argument types you pass. And it will infer the return type on its own.)
4 Likes
Hmm. OK, after playing a little bit I can see that this:
function Base.rem(x::SArray{T},y::Float64)::SArray{T} where {T}
SVector(map(x -> x%y,x))
end
works too and will work for vectors of any length. So, I think I’m confused about knowing how to declare a type correctly. How many arguments go inside the {}
and how can I figure that out?
The number (and kinds) of type parameters depends on the type. You have to look at the documentation (or the source code if there are no docs).
For example, this type has no parameters:
struct Foo
x::Float64
y::Float64
end
This has one parameter:
struct Bar{T}
x::T
y::T
end
and this has two parameters:
struct Baz{T,S}
x::T
y::S
end
In the case of StaticArrays, SVector{n,T}
is a simplified alias for SArray{Tuple{n},T,n}
, where T
is the element type and n
is the number of elements.
3 Likes
So why does this work:
function Base.rem(x::SVector,y::Float64)::SVector
SVector(map(x -> x%y,x))
end
Shouldn’t I be required to specify the type and number of elements in the type declaration?
Noted. Thank you. I guess I was just hoping to use the %
operator instead of a custom function. But I see your points
No. Any trailing type parameters that you omit can be anything.
x::SVector
means that x
can be SVector{n,T}
for any n
and T
. x::SVector{2}
means that x
can be an SVector{2,T}
for any T
.
5 Likes
Got it. Thanks for taking the time to school a newb. Much appreciated.
So is this type piracy?
struct Squares
count::Int
end
Base.iterate(S::Squares, state=1) = state > S.count ? nothing : (state*state, state+1)
If it is, what is the best way to build a custom iterator? If not, why not?
It’s only type piracy if you define iteration for types you don’t “own”. This example is fine because you own Squares
. The previous one, was redefining an operation in Base
for Struct
s defined in StaticArrays
.
4 Likes
I think I got it. So it’s type piracy even though there didn’t exist an operation in Base with type StaticArrays.
1 Like
Yes, because the function (rem
) is from Base
and the type (SVector
) is from StaticArrays
. So, if you did write this method in a package and someone loaded it, or you loaded a third-party package, and such package relied either in rem
failing for SVector
, or rem
calling a generic fallback that allowed for SVector
(like a fallback for AbstractVector
), the third-party package could start acting differently, what is considered spooky-action-at-a-distance and a bad practice.
4 Likes