Skip to content

Instantly share code, notes, and snippets.

@ToJans
Last active May 8, 2019 11:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ToJans/6995303 to your computer and use it in GitHub Desktop.
Save ToJans/6995303 to your computer and use it in GitHub Desktop.
To run, > elixir histogram.exs
defmodule Lab05 do
@doc """
My take on [the histogram exercise]
(http://computing.southern.edu/halterman/Courses/Fall2013/124/Labs/lab05_F13.html)
mentioned in [this newsgroup post]
(https://groups.google.com/d/msg/elixir-lang-talk/TTSjV0iy9yA/hpiGDZOk6DkJ)
"""
def main(), do: parse_pars(System.argv, nil)
defp parse_pars([],nil), do: histogram()
defp parse_pars(["-h"|_t],_acc), do: show_help
defp parse_pars(["--help"|_t],_acc), do: show_help
defp parse_pars(x,_acc) do
IO.puts "unexpected parameter: #{x}"
show_help
end
defp show_help() do
IO.puts """
Usage:
lab05
Options:
-h, [--help] # Show this help message and quit.
Description:
Read integers from stdin and display a histogram.
In the default, input may be terminated by a negative number
or by end-of-file (Ctrl-D).
All bits that are not parseable as integers are ignored.
This enables culling of integers from documents containing
other text.
"""
end
defp histogram() do
IO.stream(:stdio,:line)
|> Transform.extract_numbers()
|> Enum.take_while(fn (x)-> x >=0 end)
|> Transform.count_ranges
|> Output.bar_chart
end
end
defmodule Transform do
def extract_numbers(stream), do: Stream.flat_map(stream, &extract_numbers_from_line/1)
defp extract_numbers_from_line(line) do
Regex.scan( %r/(\-)?\d+/, line)
|> Stream.map(&hd/1)
|> Stream.map(&String.to_integer/1)
|> Stream.reject(fn (x) -> x == nil end)
|> Stream.map(&elem(&1, 0))
end
def count_ranges(numbers), do: Enum.reduce(numbers, init_dict, &inc_count/2)
defp init_dict(), do: Enum.map(0..5,fn (x)->{get_key(x*100),0} end) |> HashDict.new
defp inc_count(nr,dict), do: Dict.update(dict,get_key(nr),0,fn(x)->x+1 end)
defp get_key(nr) when nr < 500 do
start = div(nr,100)*100
"#{start}-#{start+99}"
end
defp get_key(_nr), do: "500+"
end
defmodule Output do
def bar_chart(data, max_bar_length//50) do
max_count = Enum.max(Dict.values(data) ++ [1])
scale = max_bar_length/max_count
Enum.each data, fn ({k,v}) ->
print_bar(k, v, scale)
end
end
defp print_bar(key,value,scale) do
bar = repeat("*",round(value*scale))
IO.puts "\t#{key}\t#{value}\t#{bar}\n"
end
defp repeat(_char,0), do: ""
defp repeat(char,length), do: Enum.map_join(1..length,"", fn (_) -> "#{char}" end)
end
Lab05.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment