Haskellで英語学習用のコマンドラインツールを実装しました

昨日のHaskellハンズオン でIOモナドを学びました。 アイデアがあったものの、イベント中に実装が間に合わなかったので改めて時間を取って実装をしてみました。

改良点すべき点は多いのですが、とりあえず動くようになったので公開してみます。

作ったもの

英語学習用のCLIツール。

日本語に対応する英語を入力すると正解か不正解か、不正解の場合はそのDiffが表示される。 f:id:kuranari_tm:20170716182029g:plain

GitHub - kuranari/ring-note

最近、瞬間英作文の書籍を使って英語の勉強をしているのですが、音読だと時制や単数・複数の扱いが曖昧なまま流してしまうので、コンソールで入力して正確な作文が出来ているのかチェックしてみることにしました。

紙に書くと時間がかかるので、キーボード入力がちょうどいいのではと思った次第です。

どんどん話すための瞬間英作文トレーニング (CD BOOK)

どんどん話すための瞬間英作文トレーニング (CD BOOK)

コード

import Control.Monad (forM_)
import Data.Maybe
import System.IO (hFlush, stdout)
import System.Console.ANSI
import System.Console.Readline (readline)
import Data.Algorithm.Diff (getGroupedDiff, Diff(First, Second, Both))

sentences :: [(String, String)]
sentences = [("これはリンゴです。", "This is an apple.")
            ,("彼は東京に住んでいます。", "He lives in Tokyo.")
            ,("これらの本はとても高い", "These books are very expensive.")
            ]

main :: IO ()
main = examination

examination :: IO ()
examination = forM_ sentences $ \(question, answer) -> do
  putStrLn question
  hFlush stdout

  maybeLine <- readline "> "
  case maybeLine of
    Nothing -> return ()
    Just line -> putResult answer line

putResult :: String -> String -> IO ()
putResult expected actual = do
  if expected == actual then
    putStrLn "✅"
  else do
    putStrLn "====="
    putStrLn expected
    mapM_ showDiff $ getGroupedDiff actual expected
    putStrLn ""
  putStrLn ""

showDiff :: Diff String -> IO ()
showDiff (First x) = do
  setSGR [SetUnderlining SingleUnderline]
  setSGR [SetColor Foreground Dull Red]
  putStr x
  setSGR [Reset]
showDiff (Second x) = do
  setSGR [SetUnderlining SingleUnderline]
  setSGR [SetColor Foreground Dull Green]
  putStr x
  setSGR [Reset]
showDiff (Both x _) = do

要素技術など

Readline

System.Console.Readline

Ctrl-aCtrl-hなどを使うために、readlineのライブラリを使いました。 OSXのインストールに手間取ったのですが、下記の方法で解決しました。

Can't pass extra cabal parameters for readline on Mac OS X · Issue #2237 · commercialhaskell/stack · GitHub

ansi-terminal

System.Console.ANSI

文字色を設定するために使用

diff

O(ND) diffアルゴリズムを使用するために使用

Diff: O(ND) diff algorithm in haskell.

エディットグラフを使用したdiffアルゴリズムとO(ND)法による実装は以下が詳しかったです。

http://hp.vector.co.jp/authors/VA007799/viviProg/doc5.htm

今回はライブラリを使ったのですが、このアルゴリズムを独自実装するのもまた楽しそうです。

感想

これまでHaskellを勉強していても、IOが必要となるアプリを作っていなかったのでいい機会となりました。

Haskellでも手続き的な記述をすることができ、これなら自分もHaskellでも意外と実用的なコード書けそうな感覚を持ちました。 引き続き実装進めていきます。

今後のTODO

  • 単語レベルでdiffを取るようにする
  • 外部ファイルから学習用データを読み込めるようにする
  • 正答数を出力する
  • 回答までの時間を計測する
  • ランダムな順番で出題できるようにする
  • 結果を保存し、間違えた問題のみ再度テスト出来るようにする
  • Ctrl-Dで終了できるようにする

Github

github.com