Skip to content

Commit ffde8e8

Browse files
Optimiere vereinigung/1 und vereinigung/2
vereinigung/1 in Shared.Zeitraum.Vereinigung ausgelagert und vereinigung/2 nutzt vereinigung/1. Optimierungen: - Timex.Interval.overlaps? eliminiert (ging über DateTime.add, Tzdata, ETS-Lookups) → ersetzt durch Tuple-Vergleich in Guards - NaiveDateTime.compare eliminiert → vorberechnete Sort-Key-Tupel - Timex.Interval.new eliminiert → direkter Struct-Bau - Enum.min/max eliminiert → max/2 auf Sort-Keys vereinigung/1 n=500: 34.676µs → 803µs (43x schneller) vereinigung/2: 13µs → 5.7µs pro Aufruf (2.3x schneller) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9f7faae commit ffde8e8

File tree

2 files changed

+55
-34
lines changed

2 files changed

+55
-34
lines changed

lib/zeitraum.ex

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ defmodule Shared.Zeitraum do
1010

1111
alias Shared.ZeitraumProtokoll
1212
alias Shared.Zeitraum.Ueberlagerung
13+
alias Shared.Zeitraum.Vereinigung
1314

1415
@type t :: ZeitraumProtokoll.t()
1516

@@ -269,16 +270,9 @@ defmodule Shared.Zeitraum do
269270
"""
270271
@spec vereinigung(t(), t()) :: t() | {t(), t()}
271272
def vereinigung(a, b) do
272-
intervall_a = als_intervall(a)
273-
intervall_b = als_intervall(b)
274-
275-
if ueberschneidung?(intervall_a, intervall_b) or grenzen_aneinander?(intervall_a, intervall_b) do
276-
from = Enum.min([intervall_a.from, intervall_b.from], NaiveDateTime)
277-
until = Enum.max([intervall_a.until, intervall_b.until], NaiveDateTime)
278-
279-
Timex.Interval.new(from: from, until: until, step: [seconds: 1])
280-
else
281-
{intervall_a, intervall_b}
273+
case vereinigung([a, b]) do
274+
[a, b] -> {a, b}
275+
[c] -> c
282276
end
283277
end
284278

@@ -305,30 +299,7 @@ defmodule Shared.Zeitraum do
305299
306300
"""
307301
@spec vereinigung([t()]) :: [t()]
308-
def vereinigung(intervalle) do
309-
intervalle
310-
|> Enum.map(&als_intervall/1)
311-
|> Enum.sort(__MODULE__)
312-
|> vereinige_sortierte_intervalle()
313-
end
314-
315-
defp vereinige_sortierte_intervalle([]), do: []
316-
defp vereinige_sortierte_intervalle([einzelnes]), do: [einzelnes]
317-
318-
defp vereinige_sortierte_intervalle([erstes, zweites | rest]) do
319-
case vereinigung(erstes, zweites) do
320-
%Timex.Interval{} = vereinigtes ->
321-
vereinige_sortierte_intervalle([vereinigtes | rest])
322-
323-
{_erstes, _zweites} ->
324-
[erstes | vereinige_sortierte_intervalle([zweites | rest])]
325-
end
326-
end
327-
328-
defp grenzen_aneinander?(intervall_a, intervall_b) do
329-
NaiveDateTime.compare(intervall_a.until, intervall_b.from) == :eq or
330-
NaiveDateTime.compare(intervall_b.until, intervall_a.from) == :eq
331-
end
302+
defdelegate vereinigung(zeitraeume), to: Vereinigung, as: :aus_zeitraeumen
332303

333304
@doc """
334305
Testet ob der beginn des ersten Zeitraums vor dem des zweiten liegt.

lib/zeitraum/vereinigung.ex

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
defmodule Shared.Zeitraum.Vereinigung do
2+
@moduledoc false
3+
4+
alias Shared.Zeitraum
5+
6+
defguardp ueberlappt(a, b) when elem(b, 0) <= elem(a, 1)
7+
defguardp endet_vor(a, b) when elem(a, 1) < elem(b, 1)
8+
defguardp beginnt_nach(a, b) when elem(a, 0) > elem(b, 0)
9+
10+
@spec aus_zeitraeumen([Zeitraum.t()]) :: [Timex.Interval.t()]
11+
def aus_zeitraeumen(zeitraeume) do
12+
zeitraeume
13+
|> Enum.map(&als_tupel/1)
14+
|> Enum.sort()
15+
|> vereinige()
16+
end
17+
18+
defp vereinige([]), do: []
19+
defp vereinige([a, b | rest]) when ueberlappt(a, b), do: vereinige([vereinige(a, b) | rest])
20+
defp vereinige([eigenstaendig | rest]), do: [intervall(eigenstaendig) | vereinige(rest)]
21+
defp vereinige(a, b) when endet_vor(a, b), do: {elem(a, 0), elem(b, 1), elem(a, 2), elem(b, 3)}
22+
defp vereinige(a, _b) when is_tuple(a), do: a
23+
24+
defp als_tupel(zeitraum) do
25+
intervall = Zeitraum.als_intervall(zeitraum)
26+
{sort_key(intervall.from), sort_key(intervall.until), intervall.from, intervall.until}
27+
end
28+
29+
defp intervall({_, _, from, until}) do
30+
%Timex.Interval{
31+
from: from,
32+
until: until,
33+
step: [seconds: 1],
34+
left_open: false,
35+
right_open: true
36+
}
37+
end
38+
39+
defp sort_key(%NaiveDateTime{
40+
year: y,
41+
month: m,
42+
day: d,
43+
hour: h,
44+
minute: min,
45+
second: s,
46+
microsecond: {us, _}
47+
}) do
48+
{y, m, d, h, min, s, us}
49+
end
50+
end

0 commit comments

Comments
 (0)