jupyter-scalaでのimport

結論

  • 使いたいライブラリは使いたいサーバでsbt publishLocalしておく
  • 例えばbreezeを使いたいなら,import $ivy.`org.scalanlp::breeze:0.13.1`のようにimportする

背景

そろそろ実際に使えるコードを書いてみようと思ったところ, あまりにもわからないことだらけでびっくりしたのですが,特にはまったのがjupyter-scala.

なぜnotebookが欲しいか

notebookとは,コードをブラウザ上で書いて実行し,その結果をブラウザ上に表示させるというのを 繰り返し実行できる仕組みで,

  • 挙動をよく知らないモジュールを確認する
  • 作ったモジュールを実際に実行する

ということをPythonではnotebookのみで実行しています. なぜなら

  • すべての実行が,再実行可能な形で残される
  • 実行結果も残るので他のメンバも結果のレビューが容易

だからです. Scalaでもぜひnotebookを使いたい.

なぜjupyter-scalaか

こちらはそんなにしっかり試したわけではないので他が良いかもですが そもそも候補は

  • jupyter-scala
  • scala-notebook
  • spark-notebook

がメジャーだと思います.もともとjupyterを使っているので,jupyter projectが 言及しているjupyter-scalaがよい,というのが主な理由です. spark-notebookは使ってみたら重くて嫌になりました.

困ったこと

さて,では実際breezeという数値計算のライブラリを使ってみようとして, そもそも仕組みがよくわかっていないことに気づくのです. そもそも,同じファイルにないものを使いたい時,どうすればよいのでしょうか? Pythonだと,

  • ライブラリはpipでインストールして,import
  • スクリプトをPYTHONPATHの下に置いておいて,import

という2種類ができたのですが,scalaだとどうすれば良いのか.sbtでコンパイルする場合

  • 1 参照先のスクリプトをpackage指定し同時にコンパイルする.参照元でimport文を書く.
  • 2 jarファイルをsrc/main/lib/の下に置いておく
  • 3 build.sbtに参照したいパッケージを書いておく (公開されているパッケージについて?)
  • 4 sbt publishLocalしておいて,参照元でimport文を書く

はできそうです.

ではjupyter-scalaで何ができるのか.

というわけで,4.しか思いつきませんでした. それぞれのサーバーでコンパイルするのでなく,jarファイルでなんとかしたいところではあります.

「Scalaスケーラブルプログラミング」9章

  • 理解
    • higher order functions : 関数を引数とする関数 により,手続きの抽象化が簡単にできる
    • カリー化: 第一引数を与えたもとで,第二引数のみを引数とする関数を定義する により, 第一引数: 普通の変数, 第二引数: 処理(関数)が, function (引数) {処理} と書ける, 特に,処理に引数が渡せていることがポイント
    • by name parameters: 引数なし関数を () => Typeではなく=> Typeとして書くこと x > 3という条件を引数として中で処理をする場合など. booleanを引数とすれば良いのではないかという話もあるが,Assertionの例で 副作用の違いが出てくる
  • わからなかった
    • 関数を引数としてとるのが役に立つとのことだが,関数を返す関数はどうか
    • なぜリスト9.1は objectか,classではないのか.変数がないからすでにobjectというのはわかる気がするが

「Scalaスケーラブルプログラミング」8章

  • 理解
    • privateメソッド: 他のクラスの名前空間をメソッドと同様に,メソッド内に関数を定義することで,他のメソッドの名前空間を汚さない
      • そうすることで,関数内の変数を使えるというメリット(関数がより単純になる)もある
    • First-class functions: 変数のようにオブジェクトとして存在できる関数のこと
      • def functionName {} ではなく, var/val functionName = Input => Output
    • よく,関数の引数に関数を渡したくなる.例えば (x: Int) => println(x)をforeachに渡す.これを短縮したいという意図から以下のルールがある
      • 引数の型 (Int)を省略可能,さらにその時()も省略可能 : 入力した変数から推測できる (できない場合エラー)
      • 引数を _ により表すことが可能
        • 型が自明でないならば,(_: Int)のように書く
        • 複数回 _が使われると,違う変数が参照される e.g. f =(_: Int) + (_:Int) f(1, 2) = 3
          • 同じものを2回参照するように使うことはできない
        • 引数全体を _ として表すことも可能
          • foreachの引数のように,関数であることが明らかならば,_も省略可能 a.foreach(println)
    • 部分適用関数: もともとある関数の一部のみを引数とするように _ で書き換える
    • closure: 中で使われている未定義変数を外で定義している関数オブジェクト
      • 変数の値が変更されれば,closureが返す値も変化する
      • しかし,closureの中で外の変数を更新していくやり方は危険である (並列にしたら訳が分からなくなる)
      • closureを次々に生成するのがよい
        • def increase (x: Int) = x + more ではなく
        • def makeIncreaser (x: Int) = (x: Int) => x + more
    • 同じ型の引数が繰り返される場合, *を使う. def f (x: Int*) = ... はInt型の未定数(0-)の長さのArrayが引数
      • しかし,その場合にArray[Int]は渡せない arr: _* とすれば渡せる
    • 再帰関数はループの最後に書くことで最適化される,ただし2つの再帰関数が依存しているような複雑な例はだめ
  • わからなかった
    • 関数内の変数を使うと,記述は単純になるがテストが大変では? なので,関数内での更新はユーザ側でやらないようにする

「Scalaスケーラブルプログラミング」7章

所感

  • 理解

    • PythonのIF式で値を返す用法はScalaにもあり,varを使わない,副作用として書かないという意味で望ましいやり方
    • whileはvarを前提として,値を返さない命令型の構文であり,望ましくない
      • 再帰で書けないか検討する
    • for は yieldで返す値を定義,結果はコレクション型.elementの型は処理に依存
      • Pythonと同じようにコレクションを逐次処理できる: element <- Array[]
        • filterもできる リスト内表記とは違う: element <- Array[] if condition(element)をfor文で受け取る
        • 複数のifを使いたい時には,間に;を用いる (最後につけるとsyntax error)
      • 入れ子は,forを2回は書かず,forの()の中にgenerator(<-)を2回書く.すると,2回目のgenerator以降が先に繰り返される
        • generatorも;で区切る必要がある
      • for 文の中で,valを省略して変数定義ができる (bind)
      • defで返り値を受け取れば,for文は関数として格納されるし,valで返り値を受け取れば,コレクションとして格納される
    • throw式もNothing型を返す,これは,ifで値を返す用法で使えるようにするのに役立つ (もちろん,値の代入が起こる前にExceptionになるが,文法エラーにはならない)
    • Finallyは値を返すべきではない,書いてもScalaでは無視される (returnを書けば強引に返せるが,混乱の元なので使わない)
    • X match {case Y => Z}で X=YのときZを返す関数, 処理のできるmapみたいな感じか
    • 変数のスコープはほぼ直感的だが,入れ子で変数を定義できる
    • リファクタリングは,等式でかける要素に分解していく,ここはPythonでもなんでも同じか
  • わからない

    • (){}の使い分け.このsectionの generator記述が() だったり {} だったりしている.
      • val a = 0; {val a=1} については再現できず,コンパイルエラーとなる

「Scalaスケーラブルプログラミング」6章

所感

  • 理解
    • immutableの欠点は,多数のデータを持つオブジェクトで一部の値の更新が頻繁にある場合. 更新のコードが煩雑になるし性能上の問題にもなりうる.
    • すべてのクラスは,デフォルトでjava.lang.Objectクラスを継承している
    • 事前条件を明記しチェックするためにrequire関数が使える
    • class MyClass(a:Int, b:Int)のa, bのは
      • コンストラクタの引数である
      • メソッドの中で参照できる
      • 外からは参照できない
    • class MyClass{val a:Int, val b:Int}のa, bは
      • 外からも参照できる
    • Pythonのselfのように,メンバXにアクセスするのにthis.Xとかける
    • 補助コンストラクタを定義できる.def this(…) ただし,基本のコンストラクタで最終的に書く必要がある(最後はthis(オリジナル引数)).
    • 例では,メンバがvalになっていることで,メンバ変数の再代入ができない,mutableになっている
    • 何も書かなくても,*で定義したメソッドが +で定義したメソッドより優先して計算される
    • 識別子の定義の仕方
      • alphanumeric 先頭はalphabetか_,その他は$以外の英数字 ($がalphabetという感覚はなかった)
      • 演算子文字 ( +-/=><?:等)
      • mixed identifiers 例:unary+ : +として働く myvar= : =として働く (アンダースコアの後に演算子)
      • literal identifiers ``で囲まれた物はなんでも識別子にできる.scalaの予約語となっているjavaのメソッドにアクセスする場合など
    • overload
      • 同じ演算子を,別の型に対しても適用可能にする
      • ただ引数を変えてメソッドを定義すれば良い
    • implicit conversion
      • implicitをつけて関数を定義すると,型の問題でメソッドが適用できなかった時に自動でその関数を使って適用できるようにすることができる
  • 補足
    • 同じ入力型,出力型を持つimplicit conversionを2つ定義すると,型変換を必要とするメソッドを定義した際にエラー(type mismatch)が出る.エラーメッセージも2つ適用できる物があるというもの
  • わからなかった点
    • 実際ライブラリを作る時に,暗黙の型変換はどこで定義すれば良い?

      つまづいた概念

  • オブジェクトグラフ
    • javaで,インスタンスを表すデータの構造のこと.

「Scalaスケーラブルプログラミング」5章

所感

  • 理解
    • ほぼ書き方の話なので読めばわかるかなと
    • 型変換して適用しなければならないように思える演算子も,自動的に適用できるようになっている. implicit conversionsとrich wrapperによる.
  • 補足
    • ==はすべてのオブジェクトに適用される,しかし当然ではあるがメンバ変数の値を見るわけではない.例えば以下を試した.
class MyClass(a: Int, b: String)
val A = new MyClass(1, "something")
val B = new MyClass(1, "something")
var C = new MyClass(1, "something")
var D = new MyClass(1, "something")

A == A // true
A == B // false
A == C // false
C == D // false`
  • わからなかった
    • シンボルリテラル,というかシンボルが何に使われるのか

「Scalaスケーラブルプログラミング」4章

所感

  • 理解
    • クラス内変数,メソッドにはself.とかなくアクセスできる
    • メソッド
      • メソッド引数は全てvalなので更新は不可
      • returnを省略すると一番最後に計算された値を返す
      • 宣言の仕方についてhttp://docs.scala-lang.org/style/declarations.html#methods
        • 返り値の型の省略は,Unitの場合と,ローカル関数の場合
        • 基本は{}は避けるが,手続きの場合{}をつけて書く
      • 等号を忘れると,返り値が自動でUnitになるので注意
    • メソッドが特殊記号だから検索が大変かも?
    • 行分割
      • javaの;は自動推定される
      • 複数行にまたがって書きたいなら,演算子を行末に置くか,かっこで囲む(または,行頭に許されない単語…覚えるの難しそう)
    • シングルトン
      • クラスに属するインスタンス全体で共有したい変数を定義したい場合
      • 名前が同じなら,companion objects,違ったらstand alone objects
    • ファイル名
    • アプリケーション
      • main関数を持つstand alone objectsを作成することで,実行可能なプログラムを作成できる
      • traitは後で詳しくということなので後で再確認
    • sbt
      • とりあえず,src/main/scalaの下において実行を確認
  • 分からなかった
    • 明示的にアクセス修飾子を指定しなければ,publicになると言われているが,一方で1章のクラス定義の例で,privateの例で何もつけられていない.
      • 実際,1章のMyClassの例でクラスを定義,インスタンスを作って変数にアクセスしようとしても出来ない
      • class ClassName(…) だとprivate, class ClassName {…} だとpublic?

つまづいた概念

  • 静的メンバ
    • インスタンスの中ではなく,クラスで共有され,更新される変数やメソッドのこと
    • main関数もそう
    • Scalaにはない