Code optimisation from the "Coding gnome"

I’m taking this training from Dave Thomas and we’re starting to create a Hanging Man game. the first part is about generating a Dictionary that reads from a .txt file and creates a list of words. Then, for every guess a function randomly takes one word from that list.
Thinking about code optimisation, we could define a single function like the one below but we would be performing very poorly, as step (1) and (2) we only need to do once and only step (3) we do multiple times, so we should have different functions for 2 main blocks: (1) create a list of words; (2) select a random word from the list. It’s just a waste to read the file and create a list of words everytime we want a random word!

#  def random_word do
#    File.read!("assets/words.txt")
#      |> String.split("\n")
#      |> Enum.random()
#  end

Also, something like his is calling the first function (that reads from File) every time:

defmodule Dictionary do
  defp word_list do
    File.read!("assets/words.txt")
      |> String.split("\n")
  end

  @spec random_word :: any
  def random_word do
    Enum.random(word_list())
  end

end

So, what would be the most efficient way to run the first step once and store it somehow and then just take a random word?

You can probably store the values into an ets if you want to improve access time, however reading from a file is essentially not very different from making a db call.

The idea of “efficiency” is fairly broad, depending on whether your app is cpu/io/memory bound.
If it isn’t, then I say go with Joe’s “make it work, then make it beautiful” :slight_smile:

2 Likes

ETS is probably the right answer, but you can set a module attribute that will create your list of words once at compile time. Then just call for a random entity from that list in your runtime functions.

defmodule Dictionary do 

  @dictionary File.read!("assets/words.txt") |> String.split("\n")

  def random_word do 
    Enum.random(@dictionary)
  end

end
3 Likes

PTS might be even better than ETS but also I agree on compile-time reading being the best option.

2 Likes

Keep reading the book or watching the video course and you will see that several approaches are explained with their trade-offs, including the one of using a module attribute.

2 Likes

One problem with this solution is that it hard wires the file name and its containing words to build time which makes it VERY locked and not possible to change at runtime.

2 Likes

Totally agree. Passing the file path to be read once to an ETS table is probably the best way, but working with ETS tables can be daunting to Elixir newcomers.

1 Like

Just as an FYI… As you work through the course, you will solve this question and some others as well. The course starts this way and you then build on this basic idea to make it both more efficient and then more useful. In a similar way, the client starts with a text UI and then advances through agent/server usage to a web UI.

2 Likes

To any newcomer reading this and feeling intimidated, you can try out GVA – a very simple wrapper around some core ETS functionality.

3 Likes