Trying to initialise a struct with a sorted Vector

Hi,

I have a struct with 2 Vector fields holding Tuples. When creating a new object I want the Vectors to be sorted but for some reason it’s not working.

abstract type Lifecycle end

struct Restorable <: Lifecycle
    health::Health
    damage_thresholds::Vector{Tuple{Percentage, Float64}}
    restoration_thresholds::Vector{Tuple{Percentage, Float64}}
end

Restorable(
    health::Real = 1;
    damage_thresholds::Vector = Vector{Tuple{Percentage,Real}}(),
    restoration_thresholds::Vector = Vector{Tuple{Percentage,Real}}(),
) = Restorable(Health(health), sort(damage_thresholds), sort(restoration_thresholds))

When I create a new Restorable with the following code:

Restorable(0.8, [(1.0, 0.2), (0.7, 0.5)], [(1.0, 0.3), (0.3, 0.1), (0.7, 0.2)])

I get:

Restorable(Health(80.0%), Tuple{Percentage,Float64}[(100.0%, 0.2), (70.0%, 0.5)], Tuple{Percentage,Float64}[(100.0%, 0.3), (30.0%, 0.1), (70.0%, 0.2)])

where the second Vector has not been sorted.

I tested whether sorting of Percentage vectors works and it does. Both with just Percentage elements and with Tuples where the first element is a Percentage. What am I missing?

For completeness, here is the code for Percentage and Health:

struct Percentage <: Real
    value::Float64
    Percentage(x) = x < 0 ? new(0) : x > 1 ? new(1) : new(round(x, digits = 6))
end

Base.show(io::IO, x::Percentage) = print(io, "$(x.value * 100)%")

Base.convert(::Type{Percentage}, x::Real) = Percentage(x)
Base.convert(::Type{Percentage}, x::Percentage) = x

Base.promote_rule(::Type{T}, ::Type{Percentage}) where T <: Real = Percentage

import Base: +, -, <, >, <=, >=, ==

+(x::Percentage, y::Percentage) = Percentage(x.value + y.value)
-(x::Percentage) = Percentage(-x.value)
-(x::Percentage, y::Percentage) = Percentage(x.value - y.value)
<(x::Percentage, y::Percentage) = x.value < y.value
<=(x::Percentage, y::Percentage) = x.value <= y.value
>(x::Percentage, y::Percentage) = x.value > y.value
>=(x::Percentage, y::Percentage) = x.value >= y.value
==(x::Percentage, y::Percentage) = x.value == y.value

Base.max(x::Percentage, y::Percentage) = Percentage(max(x.value, y.value))
Base.min(x::Percentage, y::Percentage) = Percentage(min(x.value, y.value))


mutable struct Health <: Real
    current::Percentage
    Health(current=1) = new(current)
end

Base.convert(::Type{Health}, x::Real) = Health(x)
Base.convert(::Type{Health}, x::Health) = x

Base.promote_rule(::Type{T}, ::Type{Health}) where T <: Real = Health


import Base: +, -, <, >, <=, >=, ==

+(x::Health, y::Health) = Health(x.current + y.current)
-(x::Health) = Health(-x.current)
-(x::Health, y::Health) = Health(x.current - y.current)
<(x::Health, y::Health) = x.current < y.current
<=(x::Health, y::Health) = x.current <= y.current
>(x::Health, y::Health) = x.current > y.current
>=(x::Health, y::Health) = x.current >= y.current
==(x::Health, y::Health) = x.current == y.current

Base.max(x::Health, y::Health) = Health(max(x.current, y.current))
Base.min(x::Health, y::Health) = Health(min(x.current, y.current))

Thanks in advance,
Stef

What is the output of

@which Restorable(0.8, [(1.0, 0.2), (0.7, 0.5)], [(1.0, 0.3), (0.3, 0.1)])

?

I suspect it calls the inner constructor, not the outer, hence no sorting applied.

2 Likes

Yes, there seems to be a problem with the fact that the inner and outer constructors have the same arguments. Here, in this simpler example, I get simply an error, but if I define the constructor with a different name, it works fine:

julia> struct Percentage <: Real
           value::Float64
           Percentage(x) = x < 0 ? new(0) : x > 1 ? new(1) : new(round(x, digits = 6))
       end

julia> struct A
         y :: Vector{Tuple{Percentage, Float64}}
       end

julia> A(sort([(1.0, 0.2), (0.7, 0.5)]))
A(Tuple{Percentage,Float64}[(Percentage(0.7), 0.5), (Percentage(1.0), 0.2)])

julia> AS(x) = A(sort(x))
AS (generic function with 1 method)

julia> AS([(1.0, 0.2), (0.7, 0.5)])
A(Tuple{Percentage,Float64}[(Percentage(0.7), 0.5), (Percentage(1.0), 0.2)])

julia> A(x) = A(sort(x))
A

julia> A([(1.0, 0.2), (0.7, 0.5)])
ERROR: StackOverflowError:
Stacktrace:
 [1] Array at ./boot.jl:406 [inlined]
 [2] similar at ./array.jl:375 [inlined]
 [3] copymutable at ./abstractarray.jl:971 [inlined]
 [4] #sort#8 at ./sort.jl:780 [inlined]
 [5] sort at ./sort.jl:780 [inlined]
 [6] A(::Array{Tuple{Float64,Float64},1}) at ./REPL[5]:1 (repeats 65383 times)
 [7] top-level scope at REPL[7]:1

julia>

Indeed. Didn’t think of that. Lesson learned. Thanks!