Scala日記

Scalaの備忘録。ときどき研究の話。

=> 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 については理解していたものの、() => UnitUnit => Unit の違いについてきちんと理解していなかった。何もない、という意味で ()Unitは同じものだと勘違いしており、落とし穴にハマってしまった。しかも、なんというか、引数リストの( )括弧と、値としての()と、型指定の特殊表記としての() =>の意味が重なりあって不思議な読みを引き起こしていて、話をややこしくしている。名前や記号に関する整合性を重視するScalaっぽくない仕様という感じがするが、仕方ないのかな…。

以下の解説がよくわかります。 () => Unit はFunction0が定義されるのに対して、 Unit => Unit はFunction1となるため、Unit型の値(つまり())を引数として取る必要がある。op: Unit => Unit に対してはop(())op(Unit)とすると警告が解消される。

stackoverflow.com

Unitについてはこちら。

www.scala-lang.org

Scala で 言語処理100本ノック 2015 を解く

東北大学 情報科学研究科 情報伝達学講座(乾・岡崎研究室)で作成された自然言語処理入門者のための教材「言語処理100本ノック 2015」というのがあるんですが、これを Scala で解いてみました。

github.com

だいぶん前に書いたもので、問題が公開された直後にソースコードを出すと真の学習者への効果を下げると思い、控えていたものですが、

ただ、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 の問題に関してさえも)まだ完全ではないんですが、暇を見つけて少しずつ更新していきます。

約数

素因数分解

素数

置換

分割数

フィボナッチ数列

ピタゴラス

平方数

順序集合

数列

方程式

分数

少数

数値解析

グラフ探索


これまでに解いた問題は以下のとおり。

f:id:yuimat:20151202152117p:plain

ScalaFX で sorting visualizer を 作る

ScalaGUI を作るときには、JavaFX8 compatible な ScalaFX というライブラリが使えるが、参考資料が非常に少ない。練習がてらに ScalaFX で ソーティングアルゴリズムを可視化・可聴化するプログラムを作ってみた。

github.com

動いているところ

f:id:yuimat:20151102092057j:plain f:id:yuimat:20151102092100j:plain

このプログラムは以下の動画に啓発された。本家と違うのは、音色や音階(スケール)を選んで綺麗なメロディにできること。 Pure Scalaなので、sbtとjreさえ入っていれば動く。

www.youtube.com

ついでに迷路生成・探索も実装してみた。興味のある方は是非どうぞ。

f:id:yuimat:20151102092549j:plain f:id:yuimat:20151102092553j:plain

Unicodeの拡張領域の文字を一文字とカウントする

ScalaJavaは拡張領域の文字をサロゲートペアで表すので、文字数をカウントしたいときに単純に文字列のサイズを取ると実際の文字数とずれてしまう。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]
  1. コードポイント www.wikiwand.com

  2. サロゲートペア www.wikiwand.com

UTF-8のはずのテキストの処理中に MalformedInputException で落ちる場合の対処

Webなどから取ってきた巨大な文書ファイルを処理するとき、UTF-8エンコーディングで処理したはずだったのに、中間処理に使った言語の仕様なのか処理ミスなのか、とにかく何らかの理由があって、Scalaで読み込む際に MalformedInputException が発生してしまうことがある。文字コード周りに問題があるらしいが、みんなが使っているファイルだし、1ファイルあたり圧縮済みで数十GBだし、作り直したくないな、という込み入った状況のときには以下のようにコーデックの設定でなんとかできる。

gist.github.com

Spark用sbtプロジェクトの設定

プロジェクトセットアップ

build.sbtに以下のコードを書いて、sbt update

gist.github.com

ソースを書く

gist.github.com

ビルド
sbt package
実行
spark-submit \
  --class SampleApp \
  --master yarn-cluster \
  --num-executors 8 \
  target/scala-2.10/(your project)_2.10-1.0.jar