small medium large xlarge

Dave_gnome_head_isolated_pragsmall
15 Jul 2013, 03:42
Dave Thomas (344 posts)
  • Alter the code so that successive ticks are sent to each registered client (so the first goes to the first client, the second the next client, and so on). Once the last client receives a tick, it starts back at the first. The solution should deal with new clients being added at any time.
Generic-user-small
08 Jan 2014, 07:23
Eric Liaw (3 posts)

Don’t really like the default definition of generator when the inputs are empty, but seemed to cause the least amount of duplicate code. Also, I don’t like that I used the if inside the main generator method, wasn’t sure a clean way around it though.

defmodule Tick do
  @interval 2000 # 2 seconds
  
  @name :ticker
  
  def start do
    pid = spawn(__MODULE__, :generator, [[]])
    :global.register_name(@name, pid)
  end
  
  def register(client_pid) do
    :global.whereis_name(@name) <- { :register, client_pid }
  end
  
  def generator(clients) do
    generator(clients, [], nil)
  end
  
  def generator([], [], nil) do
    receive do
      { :register, pid } ->
        IO.puts "registering #{inspect pid}"
        generator([pid], [], pid)
    after
      @interval ->
        IO.puts "tick"
        generator([], [], nil)
    end
  end
  
  def generator(active_clients, pending_clients, first_client) do
    receive do
      { :register, pid } ->
        IO.puts "registering #{inspect pid}"
        generator(active_clients, pending_clients ++ [pid], first_client)
    after
      @interval ->
        IO.puts "tick"
        [next_client | remaining_clients] = active_clients
        next_client <- {:tick}
        if next_client == first_client do
          generator(remaining_clients ++ pending_clients ++ [next_client], [], first_client)
        else
          generator(remaining_clients ++ [next_client], pending_clients, first_client)
        end
    end
  end
end

defmodule Client do
  def start do
    pid = spawn(__MODULE__, :receiver, [])
    Tick.register(pid)
  end

  def receiver do
    receive do
      { :tick } ->
        IO.puts "tock in client"
        receiver
    end
  end
end
Webcam_pragsmall
06 Mar 2014, 00:32
Andrea Bernardo Ciddio (2 posts)

What about a helper notify function:

defmodule CycleTick do
  @interval 2000
  @name :ticker

  def start do
    pid = spawn __MODULE__, :generator, [[], []]
    :global.register_name @name, pid
  end

  def register(client_pid) do
    send :global.whereis_name(@name), { :register, client_pid }
  end

  def generator(clients, waiting_clients) do
    receive do
      { :register, pid } ->
        IO.puts "registering #{inspect pid}"
        generator [pid | clients], waiting_clients
      after @interval ->
        IO.puts "tick"
        waiting_clients = notify(clients, waiting_clients)
        generator(clients, waiting_clients)
    end
  end

  def notify([], []), do: []
  def notify(clients, []), do: notify(clients, clients)

  def notify(_clients, [next_client | waiting_clients]) do
    send next_client, { :tick }
    waiting_clients
  end

end

defmodule Client do
  def start do
    pid = spawn __MODULE__, :receiver, []
    CycleTick.register pid
  end

  def receiver do
    receive do
      { :tick } ->
        IO.puts "tock in client"
        receiver
    end
  end
end
200-g_pragsmall
09 Oct 2014, 19:23
Aleksey Gureiev (11 posts)

My take:

defmodule Ticker do

  @interval 2000

  @name :ticker

  def start do
    pid = spawn __MODULE__, :generator, [[]]
    :global.register_name @name, pid
  end

  def register(client_pid) do
    send :global.whereis_name(@name), { :register, client_pid }
  end

  def generator(clients) do
    receive do
      { :register, pid } ->
        IO.puts "registering #{inspect pid}"
        generator([pid|clients])

    after
      @interval ->
        case clients do
          [] ->
            generator clients

          [client|rest] ->
            IO.puts "tick to #{inspect client}"
            send client, { :tick }
            generator :lists.append(rest, [ client ])
        end
    end
  end

end

defmodule Client do

  def start do
    pid = spawn __MODULE__, :receiver, []
    Ticker.register pid
  end

  def receiver do
    receive do
      { :tick } ->
        IO.puts "tock in client"
        receiver
    end
  end

end
Generic-user-small
11 Jan 2015, 12:21
Pierre Sugar (56 posts)
defmodule Ticker do

  @interval 2000
  @name     :ticker

  def start do
    pid = spawn(__MODULE__, :generator, [[], 0])
    :global.register_name(@name, pid)
  end

  def register(client_pid) do
    send :global.whereis_name(@name), { :register, client_pid }
  end

  def generator(clients, next_client_number) do
    receive do
      { :register, pid } ->
        IO.puts "registering #{inspect pid}"
        generator([pid|clients], next_client_number)
    after
      @interval ->
        IO.puts "tick"
        send_to_client(clients, next_client_number)
        generator(clients, next_client_number + 1)
    end
  end

  defp send_to_client(clients, next_client_number) when length(clients) > 0 do
    { :ok, client } = clients 
                      |> Enum.fetch(rem(next_client_number, length(clients)))
    send client, { :tick }
  end
  defp send_to_client([], _), do: nil
end

defmodule Client do

  def start do
    pid = spawn(__MODULE__, :receiver, [])
    Ticker.register(pid)
  end

  def receiver do
    receive do
      { :tick } ->
        IO.puts "tock in client"
        receiver
    end
  end
end
Generic-user-small
12 Jun 2015, 06:29
Jingjing Duan (3 posts)

Here’s my take:

defmodule Ticker do
  @interval 2000 # 2 seconds
  @name :ticker

  def start do
    pid = spawn(__MODULE__, :generator, [[]])
    :global.register_name(@name, pid)
  end

  def register(client_pid) do
    send :global.whereis_name(@name), {:register, client_pid}
  end

  def generator(clients) do
    receive do
      {:register, pid} ->
        IO.puts "registering #{inspect pid}"
        generator(List.insert_at(clients, -1, pid))
    after
      @interval ->
        IO.puts "tick"
        case clients do
        [head | tail] ->
          send head, {:tick}
          generator(List.insert_at(tail, -1, head))
        _ ->
          generator(clients)
        end
    end
  end
end

defmodule Client do
  def start(name) do
    pid = spawn(__MODULE__, :receive_messages, [name])
    Ticker.register(pid)
  end

  def receive_messages(name) do
    receive do
      {:tick} ->
        IO.puts "tock in #{name}"
        receive_messages(name)
    end
  end
end
Generic-user-small
07 Aug 2015, 23:36
Stefan Chrobot (10 posts)

Here’s my solution. I used a small improvement for sending of the ticks. I use a queue for handling the clients. I moved it to a separate module to implement an optimized version (avoiding ++):

defmodule Utils do
  def current_time_string do
    {_date, {h, m, s}} = :calendar.local_time
    :io_lib.format("~2..0B:~2..0B:~2..0B", [h, m, s])
  end
end

# idea by Peter Hamilton
# https://groups.google.com/d/msg/elixir-lang-talk/tJnt6TWqV-Q/v66Hogks3JcJ
defmodule Queue do
  def new do
    {[], []}
  end

  def empty?({[], []}), do: true
  def empty?(_), do: false

  def enqueue({natural_items, reversed_items}, item) do
    {natural_items, [item|reversed_items]}
  end

  def dequeue({natural_items, reversed_items}) do
    case natural_items do
      [head|tail] ->
        {head, {tail, reversed_items}}
      [] ->
        dequeue {Enum.reverse(reversed_items), []}
    end
  end
end

defmodule Ticker do
  @interval 2000
  @name   :ticker

  def start do
    ticker_pid = spawn(__MODULE__, :generator, [Queue.new])
    :global.register_name(@name, ticker_pid)
    :timer.send_interval(@interval, ticker_pid, {:do_tick})
  end
  
  def register(client_pid) do
    ticker_pid = :global.whereis_name @name
    send ticker_pid, {:register, client_pid}
  end
  
  def generator(client_pids) do
    receive do
      {:register, new_client_pid} ->
        IO.puts "registering #{inspect new_client_pid}"
        generator Queue.enqueue(client_pids, new_client_pid)
      {:do_tick} ->
        if Queue.empty?(client_pids) do
          IO.puts "#{Utils.current_time_string} - <no clients> - tick"
          generator client_pids
        else
          {next_client_pid, client_pids} = Queue.dequeue client_pids
          IO.puts "#{Utils.current_time_string} - #{inspect next_client_pid} - tick"
          send next_client_pid, {:tick}
          generator Queue.enqueue(client_pids, next_client_pid)
        end
    end
  end
end

defmodule Client do
  def start do
    client_pid = spawn(__MODULE__, :receiver, [])
    Ticker.register client_pid
  end
  
  def receiver do
    receive do
      {:tick} ->
        IO.puts "#{Utils.current_time_string} - #{inspect self} - tock"
        receiver
    end
  end
end
Generic-user-small
12 Aug 2015, 16:47
Jim Kane (6 posts)

I started out with a solution similar to Pierre’s incrementing counter, but it didn’t feel very recursive. Then I tried passing the full client list and the “remaining clients” (clients who haven’t received a tick on this iteration), which felt much more idiomatic. I think this has been my favorite exercise so far.

defmodule Ticker do

  @interval 2000 # 2 seconds
  @name     :ticker

  def start do
    pid = spawn(__MODULE__, :generator, [[], []])
    :global.register_name(@name, pid)
  end

  def register(client_pid) do
    send :global.whereis_name(@name), { :register, client_pid }
  end

  def generator(clients, remaining_clients) do
    receive do
      { :register, pid } ->
        IO.puts "registering #{inspect pid}"
        generator([pid|clients], remaining_clients)
    after @interval ->
      IO.puts "tick"
      do_tick(clients, remaining_clients)
    end
  end

  defp do_tick([], []) do
    generator([], [])
  end

  defp do_tick([ _head | _tail ] = clients, []) do
    do_tick(clients, clients)
  end

  defp do_tick(clients, remaining_clients) do
    [ client | remaining_clients ] = remaining_clients
    send client, { :tick }
    generator(clients, remaining_clients)
  end

end

defmodule Client do

  def start do
    pid = spawn(__MODULE__, :receiver, [])
    Ticker.register(pid)
  end

  def receiver do
    receive do
      { :tick } ->
        IO.puts "tock in client"
        receiver
    end
  end
end
  You must be logged in to comment