mxst
Can I have a devstruct with computed values?
Hey everyone.
I was wondering about how I should initialize a struct where I have some values and some computed values.
For now I came up with:
defmodule Stats do
defstruct annotations: 0, true_positive: 0,
false_positive: 0, false_negative: 0,
precision: 0, recall: 0
def set_precision(stats \\ %AnnotationStats{}) do
Map.put(stats, :precision, (stats.true_positive / (stats.true_positive + stats.false_positive)))
end
def set_recall(stats \\ %AnnotationStats{}) do
Map.put(stats, :recall, (stats.true_positive / (stats.true_positive + stats.false_negative)))
end
end
What I the initialize with
my_stats = %Stats{annotations: 3, true_positive: 1, false_positive: 2, false_negative: 1}
|> set_precision()
|> set_recall()
But is there a cleaner way ? Or the way to do it?
Thank you all ![]()
Marked As Solved
Eiji
Here is my proposition:
defmodule Stats do
required_keys = [:false_negative, :false_positive, :true_positive]
@enforce_keys required_keys
defstruct required_keys ++ [annotations: 0, precision: 0, recall: 0]
@type t :: %__MODULE__{
annotations: integer,
false_negative: integer,
false_positive: integer,
precision: float,
recall: float,
true_positive: integer
}
@spec init(t) :: t
def init(%__MODULE__{true_positive: true_positive} = struct) do
computed = %{
precision: init_computed(true_positive, struct.false_positive),
recall: init_computed(true_positive, struct.false_negative)
}
Map.merge(struct, computed)
end
defp init_computed(true_positive, value), do: true_positive / (true_positive + value)
end
Stats.init(%Stats{annotations: 3, false_negative: 1, false_positive: 2, true_positive: 1})
It would fail if you did not pass 3 keys (specified in @enforce_keys) which are required to compute.
Alternatively you could write something like:
defmodule Stats do
required_keys = [:false_negative, :false_positive, :true_positive]
@enforce_keys required_keys
defstruct required_keys ++ [annotations: 0, precision: 0, recall: 0]
@type t :: %__MODULE__{
annotations: integer,
false_negative: integer,
false_positive: integer,
precision: float,
recall: float,
true_positive: integer
}
@spec init(map, integer, integer, integer) :: t
def init(map, false_negative, false_positive, true_positive) do
full_map =
map
|> Map.put(:false_negative, false_negative)
|> Map.put(:false_positive, false_positive)
|> Map.put(:precision, init_computed(true_positive, false_positive))
|> Map.put(:recall, init_computed(true_positive, false_negative))
|> Map.put(:true_positive, true_positive)
struct(__MODULE__, full_map)
end
defp init_computed(true_positive, value), do: true_positive / (true_positive + value)
end
This way allows to pass map (don’t require struct), but forces to pass required keys separately.
Finally you can also do it in more pattern-matching like way:
defmodule Stats do
required_keys = [:false_negative, :false_positive, :true_positive]
@enforce_keys required_keys
defstruct required_keys ++ [annotations: 0, precision: 0, recall: 0]
@type init_map :: %{
optional(:annotations) => integer,
optional(:precision) => float,
optional(:recall) => float,
required(:false_negative) => integer,
required(:false_positive) => integer,
required(:true_positive) => integer
}
@type t :: %__MODULE__{
annotations: integer,
false_negative: integer,
false_positive: integer,
precision: float,
recall: float,
true_positive: integer
}
@spec init(init_map) :: t
def init(
%{
false_negative: false_negative,
false_positive: false_positive,
true_positive: true_positive
} = map
) do
computed = %{
precision: init_computed(true_positive, false_positive),
recall: init_computed(true_positive, false_negative)
}
__MODULE__ |> struct(map) |> Map.merge(computed)
end
defp init_computed(true_positive, value), do: true_positive / (true_positive + value)
end
Which allows to pass required keys in map.
Last Post!
Eiji
Yup, everything is correct. I saw that annotiations is not used for computing, but you pass it, so it’s more like map with optional data for me. If you want to use 2nd solution feel free to make this map optional by adding default argument like map \\ %{}.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










