まえがき
ここ数年ぼちぼちと英単語の暗記に取り組んでいるのですが、暗記カード(紙)やTOEIC対策アプリでの学習に主に効率・自由度などの面で限界を感じたので、よりオープンな暗記用ソフトウェア(SRS; Spaced Repetition learning Systems)を使用することにし、Windows, Mac, Linux, Android, iOSなど主要なプラットフォームに対応し、また定番のソフトウェアでもある Anki を新しい学習ツールとして選択しました。
Ankiはインストールした状態では空っぽで、学習するデータは自分で用意する必要があります。僕が覚えるのは英語で、英語学習に使えるデータは豊富にあります。例えば
単語
語義
などを組み合わせて「表が単語、裏が語義の暗記カード」を(論理的には)作ることが可能ですが、しかし実際にこれを簡単に行うためのツールが見つからなかったため、時間を作って作成することにしました。
要件
当初考えていたのは
- AnkiがサポートするCSV, TSVなどを扱えること。
- 外部のコマンドを叩いて、データを取得できること。
といったふんわりとした要件で、Ankiのデッキを作るツール(概案) のようなものを考えていたのですが、どうもイマイチな気がしていました。そこでさらに調査を進めるうちに驚くべきツールを発見しました。AWKです。非常に有名なツールで、僕もそれまで幾度となく触ったことがあったのにも関わらず、実際には全くこのツールの真の価値を知らなかったわけです。
AWKは正に上記の要件を満たす素晴らしいツールに思えましたが、いくつかの問題があり、結局使用することはできませんでした。しかしAWKのコンセプトは素晴らしいもので、これをそのまま取り入れ、多少の変更を加えたものを実装することにしました。
WOK
AWKはオークと呼ぶらしいです。2014年現在、これと対になるような単語は一つしか思い浮かびません。WOman Knight、すなわち女騎士です。
rubyu/wok
WOKはAWKにPython互換のCSVモジュールと、JavaとそしてScalaの豊富なライブラリを追加したものになります。
女騎士 vs オーク
Running a script
$ wok 'print("hello!")'
$ awk 'BEGIN { print "hello!" }'
Running a program file
$ wok -f program-file input
$ awk -f program-file input
Variables
$ wok -v name=value
$ awk -v name=value
Operating fields
In { _ foreach { row =>
println(row(0))
}}
{ print $1 }
Filtering data
In { _ foreach {
case row if row exists ("pattern".r.findFirstIn(_).isDefined) =>
println(row: _*)
case _ =>
}}
/pattern/ { print $0 }
Printing to a file
val path = "list" !<
In { _ foreach { _ =>
path.println(NF)
}}
{ print NF > "list" }
Executing a system command
In { _ foreach { row =>
val res = Seq("echo", row(0)) #| Seq("grep", "angel") !>
println(res.string)
println(res.code)
}}
女騎士の拡張された部分
Typed variables
# The following command is equivalent to
# var name = `value`
$ wok -v@char name=value
# The following command is equivalent to
# var name = "value"
$ wok -v@str name=value
# The following command is equivalent to
# var name = """value"""
$ wok -v@rawstr name=value
Quoting
FQ = Quote Min
OFQ = Quote All Q('"') E('\\')
Encoding
CD = Codec("UTF-8")
OCD = Codec("UTF-16")
rubyu/wok-scripts より
指定した列を元に、Epwingから辞書引きした列を追加する
eb-html.wok
#!/usr/bin/wok -f
FS = '\t'
FQ = Quote.Min
OFS = '\t'
OFQ = Quote.All
val eb = "ebquery-0.3.1.jar"
if (eb nonExistent)
eb write Resource.fromURL("https://bitbucket.org/rubyu/ebquery/downloads/ebquery-0.3.1.jar").bytes
def query(s: String) = Seq("java", "-Dfile.encoding=UTF-8", "-jar", eb, "-d", dic, "-f", "html", "-m", "tx,sb,sp,ec,ls", "--ebmap", ebmap, s).!>.string
In { _
.filter (_ isDefinedAt src)
.map (_.padTo(dst+1, ""))
.map (row => row.updated(dst, query(row(src))))
.foreach (row => println(row: _*))
}
list1.txt
a
abacus
abalone
ここでlist1.txt
を単語のリストとして、以下のコマンドを実行できます。
> java -jar wok-0.1.0.jar -f eb-html.wok -v src=0 -v dst=1 -v@rawstr dic=C:\dic\KENE7J5 -v@rawstr ebmap=C:\dic\KENE7J5.MAP list1.txt > list2.txt
list2.txt
word |
definition |
a |
a1, A1 /éı/→音声 ( as, a's, As, A's ... |
abucus |
ab・a・cus /ǽbəkəs/ ( 〜・es, ―ci... |
abalone |
ab・a・lo・ne /æ̀bəlóʊni/ 〔貝〕 ... |
指定した列に、外部の頻度順データ等でのランキング列を追加する
rank.wok
#!/usr/bin/wok -f
FS = '\t'
FQ = Quote.Min
OFS = '\t'
OFQ = Quote.All
val rank = In.from(ranking) { _
.zipWithIndex
.map { case (row, i) => row(0) -> i }
.toMap
}
In { _
.filter (_ isDefinedAt src)
.map (_.padTo(dst+1, ""))
.map { row =>
val k = row(src)
rank.get(k) match {
case Some(v) => row.updated(dst, s"${v / grp + 1}")
case None => row
}
}
.foreach (row => println(row: _*))
}
ranking.txt
a
aa
ab
aah
aardvark
abucus
abaca
abalone
ここでlist2.txt
を単語のリストとして、以下のコマンドを実行できます。
> java -jar wok-0.1.0.jar -f rank.wok -v src=0 -v dst=2 -v grp=1 -v@rawstr ranking=ranking.txt list2.txt > list3.txt
list3.txt
word |
definition |
rank |
a |
a1, A1 /éı/→音声 ( as, a's, As, A's ... |
1 |
abucus |
ab・a・cus /ǽbəkəs/ ( 〜・es, ―ci... |
6 |
abalone |
ab・a・lo・ne /æ̀bəlóʊni/ 〔貝〕 ... |
8 |
あとがき
我々には夏休みに何かを作ろうとする習性が刷り込まれているのではないか、そしてそれは義務教育が我々にもたらす最も素晴らしいものの一つではないか、などと思ったりします。
さて、夏休みどころか、さらに数ヶ月をまるまる投じて制作した本ソフトウェアですが、起動時にScalaファイルをコンパイルするためメモリを大量に消費する、同じくコンパイルのために数秒の待ち時間が発生する、記述が冗長になるなどの様々な短所はありますが、一方で、クォートが扱える、コンパイル時に型エラーがないことが保証される、ScalaのCollectionフレームワークに乗っかれるなどの長所もあります。
英語学習用デッキを生成するためのツールとして十分な機能を備えてはいますが、CUI操作に慣れていなければ難しくもあります。このあたりは今後の課題とします。