ãã ããµã¼ãã¨ã¯è¨ã£ã¦ã
Web ãµã¼ãçã§ã¯ãªããElixir ã®ãµã¼ãããã»ã¹ã®ãã¨ã§ãã®ã§ãã®ç¹ã¯ãäºæ¿ãã
å½æ°ã®ç¥æ¥ã«ã¤ãã¦ã¯ãå
é£åºããæ
å ±ãæä¾ããã¦ãã¾ãã
www8.cao.go.jp
ã¾ããç¿å¹´ã¾ã§ã®ç¥æ¥ã®ä¸è¦§ã¯ CSV å½¢å¼ã®ãã¼ã¿ã§æä¾ããã¦ãã¾ãã
ä»åã¯ãã®ãã¼ã¿ã使ã£ã¦ãæ¥ä»ããç¥æ¥ãåå¾ãããµã¼ãããã»ã¹ãä½ã£ã¦ããã¾ãã
ã¾ãæ°ããããã¸ã§ã¯ããç¨æãã¦ãã ããã
$ mix new holiday
$ cd holiday
ããããé çªã«æ©è½ã追å ãã¦ããã¾ãã
ç¥æ¥ä¸è¦§ããã¦ã³ãã¼ããã
HTTP ã¯ã©ã¤ã¢ã³ãã«ã¯ Req ãå©ç¨ãã¾ãã
hex.pm
mix.exs
ã« req
ã追å ããããã±ã¼ã¸ãåå¾ãã¾ãã
defp deps do
[
{:req, "~> 0.5"}
]
end
$ mix deps.get
IEx ä¸ã§ CSV ãã¼ã¿ããã¦ã³ãã¼ãã§ãããã¨ã確èªãã¾ãã
$ iex -S mix
iex> Req.get("https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv")
{:ok,
%Req.Response{
status: 200,
headers: %{
...
},
body: <<141, 145, 150, 175, 130, 204, 143, 106, 147, 250, 129, 69, 139, 120,
147, 250, 140, 142, 147, 250, 44, 141, 145, 150, 175, 130, 204, 143, 106,
147, 250, 129, 69, 139, 120, 147, 250, 150, 188, 143, 204, 13, 10, 49, 57,
53, 53, 47, 49, 47, 49, 44, 140, 179, 147, 250, 13, 10, 49, 57, 53, 53, 47,
49, 47, 49, 53, 44, 144, 172, 144, 108, 130, 204, 147, 250, 13, 10, 49, 57,
53, 53, 47, 51, 47, 50, 49, 44, 143, 116, 149, 170, 130, 204, 147, 250, 13,
10, 49, 57, 53, 53, 47, 52, 47, 50, 57, 44, 147, 86, 141, 99, 146, 97, 144,
182, 147, 250, 13, 10, 49, 57, ...>>,
trailers: %{},
private: %{}
}}
ãã¼ã¿ã¯åå¾ã§ãã¾ãããã¨ã³ã³ã¼ãã£ã³ã°ã UTF-8 ã§ãªããããå
·ä½çã«ã¯ SHIFT JIS ã§ããããã«ããã®ã¾ã¾ã§ã¯ Elixir ã®æååã¨ãã¦æ±ãã¾ããã
UTF-8 ã«å¤æãããã« iconv ãå©ç¨ãã¾ãã
hex.pm
iconv 㯠NIF ãå©ç¨ãã¦ãã¾ãã®ã§ã¯ãã¹ç°å¢ã§éçºããå ´åã¯æ³¨æãå¿
è¦ã§ãã
ã¡ãªã¿ã« iconv 㯠Erlang ã®ããã±ã¼ã¸ã§ããããã¢ã¸ã¥ã¼ã«å㯠:iconv
ã«ãªãã¾ãããå¼æ°ã¯ãã¤ããªã§ä¸ãããã Elixir ã®é¢æ°ã¨åãæè¦ã§å©ç¨ãããã¨ãã§ãã¾ãã
mix.exs
ã« iconv
ã追å ããããããã±ã¼ã¸ãåå¾ã IEx ãèµ·åãã¾ãã
defp deps do
[
{:req, "~> 0.5"},
{:iconv, "~> 1.0"}
]
end
$ mix deps.get
$ iex -S mix
ãã¦ã³ãã¼ããããã¼ã¿ã :iconv.convert/3
ã§å¤æãã¾ãã
iex> {:ok, resp} = Req.get("https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv")
iex> :iconv.convert("cp932", "utf-8", resp.body)
"å½æ°ã®ç¥æ¥ã»ä¼æ¥ææ¥,å½æ°ã®ç¥æ¥ã»ä¼æ¥å称\r\n1955/1/1,å
æ¥\r\n1955/1/15,æ人ã®æ¥\r\n1955/3/21,æ¥åã®æ¥\r\n1955/4/29,天çèªçæ¥\r\n1955/5/3,æ²æ³è¨å¿µæ¥\r\n1955/5/5,...
CSV ããã¼ã¹ãã
CSV ã®ãã¼ã¹ã«ã¯ NimbleCSV ãå©ç¨ãã¾ãã
hex.pm
defp deps do
[
{:req, "~> 0.5"},
{:iconv, "~> 1.0"},
{:nimble_csv, "~> 1.2"}
]
end
$ mix deps.get
$ iex -S mix
å®ã¯ã
ããã§ä¸ã¤åé¡ãçºçãã¾ãã
iex> {:ok, resp} = Req.get("https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv")
{:ok,
%Req.Response{
status: 200,
headers: %{
...
},
body: [
[
<<141, 145, 150, 175, 130, 204, 143, 106, 147, 250, 129, 69, 139, 120,
147, 250, 140, 142, 147, 250>>,
<<141, 145, 150, 175, 130, 204, 143, 106, 147, 250, 129, 69, 139, 120,
147, 250, 150, 188, 143, 204>>
],
["1955/1/1", <<140, 179, 147, 250>>],
["1955/1/15", <<144, 172, 144, 108, 130, 204, 147, 250>>],
["1955/3/21", <<143, 116, 149, 170, 130, 204, 147, 250>>],
["1955/4/29", <<147, 86, 141, 99, 146, 97, 144, 182, 147, 250>>],
["1955/5/3", <<140, 155, 150, 64, 139, 76, 148, 79, 147, 250>>],
...
è¦ã¦ã®éã Req.get/1
ã§åå¾ãããã¼ã¿ã CSV ã¨ãã¦ãã¼ã¹æ¸ã¿ã¨ãªã£ã¦ãã¾ãã
以åè¨äºã«æ¸ããããã«ãReq 㯠NimbleCSV ã¨ä¸ç·ã«å©ç¨ããã°ãããã³ã³ãã³ãã®ç¨®é¡ã CSV ã§ããã¨èªåçã«ãã¼ã¹ãã¦ãã¾ãã¾ãã
blog.emattsan.org
ãªã¹ãã®ãªã¹ãã«å解ãããåè¦ç´ ãã¨ã«ã¨ã³ã³ã¼ãã£ã³ã°ãå¤æãããã¨ãã§ãã¾ãããæéãèããã¨åå¾ãããã¤ããªãã¼ã¿ã®ã¨ã³ã³ã¼ãã£ã³ã°ãä¸æ¬ã§å¤æãã¦ãããã¼ã¹ããã®ãããããã§ãã
èªåçã«ãã¼ã¹ãããã®ãé²ãã«ã¯ Req.get/2
ã® :decode_body
ãªãã·ã§ã³ã« false
ãæå®ãã¾ãã
iex> {:ok, resp} = Req.get("https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv", decode_body: false)
ã¾ã NimbleCSV ã¯ãCSV ãã¼ãµãå®ç¾©ããããã±ã¼ã¸ãã§ãããããããããããã¼ãµãå®ç¾©ããå¿
è¦ãããã¾ãã
iex> NimbleCSV.define(Holiday.Parser, [])
ãã㧠CSV ãã¼ãµã¢ã¸ã¥ã¼ã« Holiday.Parser
ãå©ç¨ã§ããããã«ãªãã¾ããã
第äºå¼æ°ã®ãªãã·ã§ã³ã§ãã¼ãµã®ãµãã¾ããæå®ãããã¨ãã§ãã¾ãããä»åã¯ç¹å¥ãªãµãã¾ãã¯ä¸è¦ã§ãã®ã§ç©ºã§æå®ãã¦ãã¾ãã
ãã¦ã³ãã¼ããã CSV ãã¼ã¿ã :iconv
ã§ã¨ã³ã³ã¼ãã£ã³ã°å¤æããå®ç¾©ãããã¼ãµ Holiday.Parser
ã§ãã¼ã¹ãã¾ãã
iex> Holiday.Parser.parse_string(:iconv.convert("cp932", "utf-8", resp.body))
[
["1955/1/1", "å
æ¥"],
["1955/1/15", "æ人ã®æ¥"],
["1955/3/21", "æ¥åã®æ¥"],
...
ç¡äºãUTF-8 ã®æååã®ãªã¹ãã®ãªã¹ããå¾ããã¨ãã§ãã¾ããã
ãã¼ã¿ã ETS ã«æ ¼ç´ãã
æ¤ç´¢ãç°¡åã«ããããã«ãä»å㯠ETS ãå©ç¨ãããã¨ã«ãã¾ãã
www.erlang.org
ç°¡åã«ä½¿ãæ¹ããããããã¾ãã
ã¾ããããã»ã¹ãèµ·åãã¾ãã
iex> table = :ets.new(:holiday, [])
#Reference<0.2088135362.3750625287.239169>
次ã«ãã¼ã¿ãæå
¥ãã¾ãã
ãã¼ã¿ã¯ã¿ãã«ã§ããå¿
è¦ãããã¾ãã
ã¾ãã¿ãã«ã®ä¸çªæåã®è¦ç´ ããã¼ã«ãªãã¾ãã
ããã§ã¯ Erlang å½¢å¼ã®æ¥ä»ãã¼ã¿ï¼å¹´ææ¥ã®æ°å¤ãããªãã¿ãã«ï¼ {2024, 8, 11}
ããã¼ã«ãªãã¾ãã
iex> :ets.insert(table, {{2024, 8, 11}, "å±±ã®æ¥"})
true
æ¤ç´¢ãã¦ãã¼ã¿ãåå¾ãã¾ãã
iex> :ets.select(table, [{{{2024, 8, 11}, :"$1"}, [], [:"$1"]}])
["å±±ã®æ¥"]
ETS ãæ¬é ãããä¸çªã®åå ãããã®æ¤ç´¢æ¸å¼ã®é¢å¦ãã§ã¯ãªããã¨æãããã®ã§ããã
ä»åããé¢æ°å½¢å¼ããæ¤ç´¢æ¸å¼ã«å¤æãã¦ããã :ets.fun2ms
ã®å©ããåãã¦åãæãããã¨ã«ãã¾ãã
ããã§ä½¿ã£ãæ¤ç´¢æ¸å¼ã¯æ¬¡ã®ããã«ãã¦å¾ããã¨ãã§ãã¾ãã
iex> :ets.fun2ms(fn {{2024, 8, 11}, name} -> name end)
[{{{2024, 8, 11}, :"$1"}, [], [:"$1"]}]
ã§ã¯ããã¼ã¿ãæå
¥ãã¦ããã¾ãã
åç¥æ¥ã®æ¥ä»ã¯ String.split/2
ã§åå²ã String.to_integer/1
ã§æ´æ°å¤ã«å¤æãã¾ãã
ãããã¨ååãåããã¦ä¸ã¤ã®ã¿ãã«ã®å½¢å¼ã«å¤æã :ets.insert/2
ã§ç»é²ãã¾ãã
iex> Holiday.Parser.parse_string(:iconv.convert("cp932", "utf-8", resp.body))
iex> |> Enum.each(fn [date, name] ->
...> [year, month, day] =
...> date
...> |> String.split("/")
...> |> Enum.map(&String.to_integer/1)
...> :ets.insert(table, {{year, month, day}, name})
...> end)
ããã¤ãæ¤ç´¢ãã¦ã¿ã¾ãã
iex> :ets.select(table, [{{{2024, 8, 11}, :"$1"}, [], [:"$1"]}])
["å±±ã®æ¥"]
iex> :ets.select(table, [{{{2024, 1, 1}, :"$1"}, [], [:"$1"]}])
["å
æ¥"]
ãã¾ãæå
¥ã§ããããã§ãã
ãã¼ã¿ãæ¤ç´¢ãã
åç´ã«ãã¼ã«ãªãå¹´ææ¥ãæå®ãã¦æ¤ç´¢ããã ãã§ãªããããå°ãè¤éãªæ¤ç´¢ããããã¨ãã§ãã¾ãã
ãã¨ãã° 2024 å¹´ 5 æã®ç¥æ¥ããã¹ã¦åå¾ãã¦ã¿ã¾ãã
:ets.fun2ms/2
ã§æ¤ç´¢ã®æ¸å¼ã調ã¹ã¾ãã
iex> :ets.fun2ms(fn {{2024, 5, d}, n} -> {{2024, 5, d}, n} end)
[{{{2024, 5, :"$1"}, :"$2"}, [], [{{{{2024, 5, :"$1"}}, :"$2"}}]}]
ããã :ets.select/2
ã«æå®ãã¦æ¤ç´¢ãã¾ãã
iex> :ets.select(table, [{{{2024, 5, :"$1"}, :"$2"}, [], [{{{{2024, 5, :"$1"}}, :"$2"}}]}])
[
{{2024, 5, 6}, "ä¼æ¥"},
{{2024, 5, 3}, "æ²æ³è¨å¿µæ¥"},
{{2024, 5, 4}, "ã¿ã©ãã®æ¥"},
{{2024, 5, 5}, "ãã©ãã®æ¥"}
]
5 æã®ç¥æ¥ã®ä¸è¦§ãåå¾ãããã¨ãã§ãã¾ããã
â¦ãã
é åºãæ¥ä»é ã«ãªã£ã¦ãã¾ããã
ETS ã¯ç¡æå®ã§ã¯é åºãèæ
®ããªããã¨ãåå ã§ãã
ãã㯠:ets.new/2
ã®ãªãã·ã§ã³ã« :ordered_set
ãæå®ãããã¨ã§è§£æ±ºãã¾ãã
iex> table = :ets.new(:hoiday, [:ordered_set])
ããã§æºåãæ´ãã¾ããã
ãµã¼ããä½ã
ãã¨ã¯ããã¾ã§ã®è¦ç´ ããã¹ã¦ä¸ã¤ã«ã¾ã¨ããã ãã§ãã
å®çªã® GenServer ã使ã£ã¦ãµã¼ãã«ä»ç«ã¦ã¾ãã
defmodule Holiday do
use GenServer
NimbleCSV.define(Holiday.Parser, [])
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts)
end
def lookup(pid, year, month, day) do
case GenServer.call(pid, {:lookup, year, month, day}) do
[result] ->
result
[] ->
nil
end
end
def lookup(pid, year, month) do
GenServer.call(pid, {:lookup, year, month, :"$3"})
end
def lookup(pid, year) do
GenServer.call(pid, {:lookup, year, :"$2", :"$3"})
end
def init() do
Process.send_after(self(), :init_table, 0)
table = :ets.new(:holiday, [:ordered_set])
{:ok, %{table: table}}
end
def handle_call({:lookup, year, month, day}, , state) do
match_spec = [{{{year, month, day}, :"$4"}, [], [{{{{year, month, day}}, :"$4"}}]}]
result =
:ets.select(state.table, match_spec)
|> Enum.map(fn {date, name} ->
{Date.from_erl!(date), name}
end)
{:reply, result, state}
end
def handle_info(:init_table, state) do
{:ok, resp} =
"https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"
|> Req.get(decode_body: false)
:iconv.convert("cp932", "utf-8", resp.body)
|> Holiday.Parser.parse_string()
|> Enum.each(fn [date, name] ->
[year, month, day] =
date
|> String.split("/")
|> Enum.map(&String.to_integer/1)
:ets.insert(state.table, {{year, month, day}, name})
end)
{:noreply, state}
end
end
ã¤ã³ã¿ãã§ã¼ã¹ã«ã¯ãå¹´ã®ã¿ãå¹´æã®ã¿ãå¹´ææ¥ãæå®ã§ãã Holiday.lookup/1,2,3
ã®ä¸ç¨®é¡ãç¨æãã¾ããã
ã¾ãå¹´ææ¥ãæå®ããå ´åã¯ãªã¹ãã§ãªãè¦ã¤ãã£ãè¦ç´ ãã®ãã®ãè¿ãããã«ãã¦ãã¾ãã
å ãã¦æ¥ä»ã¯ Date
åã«å¤æãããã¨ã«ãã¾ããã
åæåæã« Process.send_after/2
ã使ã£ã¦ãµã¼ãèªèº«ã«ã¡ãã»ã¼ã¸ãéããéåæã§ãã¦ã³ãã¼ããã¦ãã¾ãã
ããã«ããåæåå¦çããããã¯ãããã¨ãé²ãã§ãã¾ãã
ãµã¼ããèµ·åããã¨ãã¦ã³ãã¼ããå®è¡ãããã®ã§ã注æãã ããã
åä½ã確èªãã¾ãã
$ iex -S mix
Holiday_start/0
ã§ãµã¼ããèµ·åãã¾ãã
iex> {:ok, pid} = Holiday.start_link()
å¹´ææ¥ãæå®ãã¦ãã¼ã¿ãåå¾ãã¾ãã
æå®ããæ¥ä»ãç¥æ¥ã§ãªãå ´å㯠nil
ãè¿ãã¾ãã
iex> Holiday.lookup(pid, 2024, 8, 11)
{~D[2024-08-11], "å±±ã®æ¥"}
iex> Holiday.lookup(pid, 2024, 8, 12)
{~D[2024-08-12], "ä¼æ¥"}
iex> Holiday.lookup(pid, 2024, 8, 13)
nil
å¹´æã®ã¿ãæå®ãã¦ãã¼ã¿ãåå¾ãã¾ãã
ç¥æ¥ããªãæãæå®ãããå ´åã¯ç©ºã®ãªã¹ããè¿ãã¾ãã
iex> Holiday.lookup(pid, 2024, 8)
[{~D[2024-08-11], "å±±ã®æ¥"}, {~D[2024-08-12], "ä¼æ¥"}]
iex> Holiday.lookup(pid, 2024, 6)
[]
å¹´ã®ã¿ãæå®ãã¦ãã¼ã¿ãåå¾ãã¾ãã
iex> Holiday.lookup(pid, 2024)
[
{~D[2024-01-01], "å
æ¥"},
{~D[2024-01-08], "æ人ã®æ¥"},
{~D[2024-02-11], "建å½è¨å¿µã®æ¥"},
{~D[2024-02-12], "ä¼æ¥"},
{~D[2024-02-23], "天çèªçæ¥"},
{~D[2024-03-20], "æ¥åã®æ¥"},
{~D[2024-04-29], "æåã®æ¥"},
{~D[2024-05-03], "æ²æ³è¨å¿µæ¥"},
{~D[2024-05-04], "ã¿ã©ãã®æ¥"},
{~D[2024-05-05], "ãã©ãã®æ¥"},
{~D[2024-05-06], "ä¼æ¥"},
{~D[2024-07-15], "æµ·ã®æ¥"},
{~D[2024-08-11], "å±±ã®æ¥"},
{~D[2024-08-12], "ä¼æ¥"},
{~D[2024-09-16], "æ¬èã®æ¥"},
{~D[2024-09-22], "ç§åã®æ¥"},
{~D[2024-09-23], "ä¼æ¥"},
{~D[2024-10-14], "ã¹ãã¼ãã®æ¥"},
{~D[2024-11-03], "æåã®æ¥"},
{~D[2024-11-04], "ä¼æ¥"},
{~D[2024-11-23], "å¤å´æè¬ã®æ¥"}
]
ãã¾ããã£ãããã§ãã
ãã¤ãèªãã¯ãã£ã¨èªã¾ãªãï¼å
¨å²ãªãã¬åå²
Homo sapiens ãç¾ããã¾ã§ã«ã©ã®ããã«åå²ãã¦ããã®ãã
ä»ã®çãç©ã¨ã®çç©ã¨ãã¦ã®è·é¢ããæ¥å¸¸çã«æãããã®ã¨ã¯ãéã£ã¦ãããããªãã£ããã