はじめに
自然言語処理と Python のトレーニングのため、 東北大学の乾・岡崎研究室 Web ページにて公開されている言語処理100本ノックを Elixir で挑戦していきます。
第2章: UNIXコマンドの基礎
hightemp.txt は,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである. 以下の処理を行うプログラムを作成し, hightemp.txt を入力ファイルとして実行せよ.さらに,同様の処理を UNIX コマンドでも実行し,プログラムの実行結果を確認せよ.
10. 行数のカウント
行数をカウントせよ.確認には wc コマンドを用いよ.
filepath = "hightemp.txt"
File.stream!(filepath)
|> Enum.to_list
|> length
|> IO.inspect
11. タブをスペースに置換
タブ 1 文字につきスペース 1 文字に置換せよ.確認には sed コマンド, tr コマンド,もしくは expand コマンドを用いよ.
filepath = "hightemp.txt"
File.read!(filepath)
|> String.replace("\t", " ")
|> IO.inspect
12. 1列目をcol1.txtに,2列目をcol2.txtに保存
各行の 1 列目だけを抜き出したものを col1.txt に,2列目だけを抜き出したものを col2.txt としてファイルに保存せよ. 確認には cut コマンドを用いよ.
filepath = "hightemp.txt"
# 空のファイルの作成
File.write!("col1.txt", "", [:write])
File.write!("col2.txt", "", [:write])
File.stream!(filepath)
|> Enum.each(fn line ->
[col1, col2 | _] = String.split(line, "\t")
File.write!("col1.txt", col1 <> "\n", [:append])
File.write!("col2.txt", col2 <> "\n", [:append])
end)
13. col1.txtとcol2.txtをマージ
12 で作った col1.txt と col2.txt を結合し,元のファイルの 1 列目と 2 列目を タブ区切りで並べたテキストファイルを作成せよ.確認には paste コマンドを用いよ.
col1 = File.stream!("col1.txt")
col2 = File.stream!("col2.txt")
content = Stream.zip(col1, col2)
|> Stream.map(fn tuple ->
tuple |> Tuple.to_list |> Enum.map(&String.trim/1) |> Enum.join("\t")
end)
|> Enum.join("\n")
File.write!("merge.txt", content, [:write])
14. 先頭からN行を出力
自然数 N をコマンドライン引数などの手段で受け取り,入力のうち先頭の N 行だけを表示せよ. 確認には head コマンドを用いよ.
filepath = "hightemp.txt"
n = hd(System.argv) |> String.to_integer
File.stream!(filepath)
|> Enum.take(n)
|> IO.inspect
15. 末尾のN行を出力
自然数 N をコマンドライン引数などの手段で受け取り,入力のうち末尾の N 行だけを表示せよ. 確認には tail コマンドを用いよ.
filepath = "hightemp.txt"
n = hd(System.argv) |> String.to_integer
File.stream!(filepath)
|> Enum.take(-n)
|> IO.inspect
16. ファイルをN分割する
自然数 N をコマンドライン引数などの手段で受け取り,入力のファイルを行単位で N 分割せよ. 同様の処理を split コマンドで実現せよ.
filepath = "hightemp.txt"
file_n = hd(System.argv) |> String.to_integer
content = File.stream!(filepath) |> Enum.to_list
max_row = div(length(content), file_n)
0..(file_n - 1)
|> Enum.map(fn n ->
sliced_content = Enum.slice(content, (n * max_row)..((n+1) * max_row) - 1)
File.write!("slice#{n}.txt", sliced_content, [:write])
end)
17. 1列目の文字列の異なり
1 列目の文字列の種類(異なる文字列の集合)を求めよ.確認には sort , uniq コマンドを用いよ.
filepath = "hightemp.txt"
File.stream!(filepath)
|> Enum.map(fn line -> hd(String.split(line, "\t")) end)
|> MapSet.new
|> MapSet.to_list
|> IO.inspect
# => ["千葉県", "和歌山県", "埼玉県", "大阪府", "山形県", "山梨県",
# "岐阜県", "愛媛県", "愛知県", "群馬県", "静岡県", "高知県"]
18. 各行を3コラム目の数値の降順にソート
各行を 3 コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ). 確認には sort コマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).
filepath = "hightemp.txt"
File.stream!(filepath)
|> Enum.sort_by(fn line ->
[_, _, col3 | _] = String.split(line, "\t")
col3
end, &>=/2)
|> Enum.join("\n")
|> IO.puts
# => 高知県 江川崎 41 2013-08-12
# 埼玉県 熊谷 40.9 2007-08-16
# 岐阜県 多治見 40.9 2007-08-16
# :
# 山梨県 大月 39.9 1990-07-19
# 山形県 鶴岡 39.9 1978-08-03
# 愛知県 名古屋 39.9 1942-08-02
19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる
各行の 1 列目の文字列の出現頻度を求め,その高い順に並べて表示せよ. 確認には cut , uniq , sort コマンドを用いよ.
filepath = "hightemp.txt"
File.stream!(filepath)
|> Enum.map(fn line -> hd(String.split(line, "\t")) end)
|> Enum.reduce(%{}, fn word, acc -> Map.update(acc, word, 1, &(&1 + 1)) end)
|> Enum.sort_by(fn {_k, v} -> v end, &>=/2)
|> IO.inspect
# => [{"埼玉県", 3}, {"山形県", 3}, {"山梨県", 3}, {"群馬県", 3},
# {"千葉県", 2}, {"岐阜県", 2}, {"愛知県", 2}, {"静岡県", 2},
# {"和歌山県", 1}, {"大阪府", 1}, {"愛媛県", 1}, {"高知県", 1}]
第3章につづく (後日追記:続きません)