=> Unit と () => Unit と Unit => Unit の違い
先日、op: Unit => Unit
と書かざるを得ない状況に直面した。それで、この関数を op()
として呼び出したときに「多分あなたが意図していることと違うことしてますよ」みたいなwarningが出て気づいたこと。ScalaのUnitは値がないことを表す型なわけだが、「実際に値がない」のではなく、「値がないことを仮想的に表す値()
」を持つ型であるということ。
具体的なwarningメッセージはこちら。
Warning:(140, 9) Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want. signature: Function1.apply(v1: T1): R given arguments: <none> after adaptation: Function1((): Unit) op() ^
したがって、Unit => Unit
は実際には一つの引数を取って一つの値を返す関数であり、引数を取らない () => Unit
とは別物である。そのため、op()
のように引数が空っぽの呼び出しでは整合性が取れない。
=> Unit
と () => Unit
については理解していたものの、() => Unit
と Unit => Unit
の違いについてきちんと理解していなかった。何もない、という意味で ()
とUnit
は同じものだと勘違いしており、落とし穴にハマってしまった。しかも、なんというか、引数リストの( )
括弧と、値としての()
と、型指定の特殊表記としての() =>
の意味が重なりあって不思議な読みを引き起こしていて、話をややこしくしている。名前や記号に関する整合性を重視するScalaっぽくない仕様という感じがするが、仕方ないのかな…。
以下の解説がよくわかります。
() => Unit
はFunction0が定義されるのに対して、 Unit => Unit
はFunction1となるため、Unit型の値(つまり()
)を引数として取る必要がある。op: Unit => Unit
に対してはop(())
や op(Unit)
とすると警告が解消される。
Unitについてはこちら。
Scala で 言語処理100本ノック 2015 を解く
東北大学 情報科学研究科 情報伝達学講座(乾・岡崎研究室)で作成された自然言語処理入門者のための教材「言語処理100本ノック 2015」というのがあるんですが、これを Scala で解いてみました。
だいぶん前に書いたもので、問題が公開された直後にソースコードを出すと真の学習者への効果を下げると思い、控えていたものですが、
ただ、4月からやっている研究室などではさすがに終わっているだろうし、そもそもScalaを書こうという人は初学者ではないだろうというこで、初学者以外の学習効率を上げるための参考資料として、また、言語処理とScalaの裾野を広げるために公開しておきます。
主なターゲット層は
- 「Python で一周した。Scalaではどう書くのか知りたい」
- 「自然言語処理については大体分かっている。Scalaを学びたい」
- 「コップ本を読んでいる(あるいはもう読んだ)。実践的なコードを見ながら学習したい」
などの人です。言語処理100本ノック 2015 が初めての人は、まずは自分の得意な言語で一度解いてみることをおすすめします。特にこれから自然言語処理を学びたいという人にとっては、試行錯誤の過程で色々なことが身につく大変良い教材になっているので、自分でやってみることに大きな意義があると思います。
コードには「Scalaではこう書くだろう」を密に詰め込んでいますが、解説はありません。「Scalaをこれから学ぶ」という人などにも、コップ本を片手に読むと文法と実践的なコードが結びついて理解が深まるのではないかな、と想像しています。
Project Eulerで用いた数学知識
オイラーにちなんだ数学的なプログラミングの問題が出題されるサイト、Project Euler。 大体の問題は、greedy にやっても解けるんですが、適切な数学的知識を用いると時間計算量のオーダーが劇的に変わってきます。 そこで、100問目まではラップトップで1秒以内(もちろんScalaです)に解くという縛りを設けてやっていました。 (ただし、幾つかの問題ではこの縛りに違反したまま。 Problem 60: 9,472ms, Problem 78: 2,948ms, Problem 86: 2,125ms, Problem 93: 2,244ms, Problem 95: 3,854ms, Problem 96: 1,184ms)
その際に使った知識(Wikipediaへのリンク)をここにまとめていきます。 このリストは(これまでに解いた Project Euler の問題に関してさえも)まだ完全ではないんですが、暇を見つけて少しずつ更新していきます。
約数
素因数分解
素数
置換
分割数
フィボナッチ数列
ピタゴラス数
平方数
順序集合
数列
方程式
分数
少数
数値解析
グラフ探索
これまでに解いた問題は以下のとおり。
Unicodeの拡張領域の文字を一文字とカウントする
ScalaやJavaは拡張領域の文字をサロゲートペアで表すので、文字数をカウントしたいときに単純に文字列のサイズを取ると実際の文字数とずれてしまう。Java 1.5からはUnicodeのコードポイント を数えるメソッドが追加されているので、これを使えば正確に文字数をカウントできる。
以下、地球の絵文字を例に取って説明。
scala> val earth = "\uD83C\uDF0D" earth: String = 🌍 scala> println(earth, earth.length) (🌍,2) scala> earth.codePointCount(0, earth.length) res20: Int = 1
ちなみに、
Python
% ipython In [1]: print u"\U0001F30D", len(u"\U0001F30D") 🌍 2 % ipython3 In [1]: print(u"\U0001F30D", len(u"\U0001F30D")) 🌍 1
Ruby
% pry [1] pry(main)> p ["\u{1F30D}", "\u{1F30D}".size] ["🌍", 1]
コードポイント www.wikiwand.com
UTF-8のはずのテキストの処理中に MalformedInputException で落ちる場合の対処
Spark用sbtプロジェクトの設定
プロジェクトセットアップ
build.sbtに以下のコードを書いて、sbt update
ソースを書く
ビルド
sbt package
実行
spark-submit \ --class SampleApp \ --master yarn-cluster \ --num-executors 8 \ target/scala-2.10/(your project)_2.10-1.0.jar