前回の記事でChisel Bootcampで勉強を進めていくための準備を行い、jupyter notebook上でのScalaコードの実行を確認した。
ということで今回からは本格的にChisel Bootcampに取り組んでいく。
Module 1: Scala入門
この章の目的は以下の通り。
これまでScalaの学習でやってきたように、Bootcampに記載されているコードを実際に動かしながら学習を進めていく。
、、、が、かなりの部分が被るのでそのへんはサクッと見ていく。
Scalaの理解
とりあえず、"Scalaの理解"と題して書いてある部分は引用しておく。
Scalaは共通のプログラミングパラダイムとはまた違ったプログラミング言語です。
私達がこれを使うことにした理由はいくつかあります:
- DSLを動かすのに良い言語である様々なデータ型を操作するための強力で整然としたライブラリを持つ
- 巨大なクラスのエラーを開発のごく初期段階で検出するのを手助けする型厳密なシステムを持つ(例えばコンパイル)
- 強力な表現記法と関数を渡す方法がある
- Chipel, Chijel, Chic*el、、発音しづらいが、それと同じくらいにChiselは良い
上記のポイントはこの後Chiselを学べば明白になって来るが、今は基本的なScalaのコードを読むこと、そして書くことに集中しよう。
変数と定数 - var と val
以前にもやっているのでポイントだけ。
var
は変数(mutable)val
は定数(immutable)- 可能な限り
val
を使うことを意識して学んでいこう
最初の2つは知っていることだが、3番めの”val
を使っていく”というのは意識してなかった。理由だが、以下のように書いてある。
多くの場合、変数を再利用する機会を減らすことは、エラーを産んだりコードを複雑化するのを防ぐ方法である。
Scalaの構造はこの練習をするのに向いているので、あなたが思っている以上に簡単なはずだ。
一応、Bootcampのサンプルコードを実行していおく
var numberOfKittens = 6 val kittensPerHouse = 101 val alphabet = "abcdefghijklmnopqrstuvwxyz" var done = false numberOfKittens += 1 // kittensPerHouse = kittensPerHouse * 2 // This would not compile; kittensPerHouse is not updatable println(alphabet) done = true
実行結果は以下のようになる。
numberOfKittens: Int = 6 kittensPerHouse: Int = 101 alphabet: String = "abcdefghijklmnopqrstuvwxyz" done: Boolean = false abcdefghijklmnopqrstuvwxyz
条件式
Scalaも他の言語同様に条件分岐を実装可能だ。
// A simple conditional; by the way, this is a comment if (numberOfKittens > kittensPerHouse) { println("Too many kittens!!!") } // The braces are not required when all branches are one liners. However, the // Scala Style Guide prefers brace omission only if an "else" clause is included. // (Preferably not this, even though it compiles...) if (numberOfKittens > kittensPerHouse) println("Too many kittens!!!") // ifs have else clauses, of course // This is where you can omit braces! if (done) println("we are done") else numberOfKittens += 1 // And else ifs // For style, keep braces because not all branches are one liners. if (done) { println("we are done") } else if (numberOfKittens < kittensPerHouse) { println("more kittens!") numberOfKittens += 1 } else { done = true }
実行結果
we are done we are done
ポイントは以下
//
でコメントif
式の中身が一つの式なら{}
は不要(Cとかと一緒)else
/else if
でif
に当てあはまらない場合の処理を実装できる- 一つ上のコードブロックで
done = true
を行っているので"we are done"が2回表示される
以前のScalaの勉強でやったとおり、if
式になるのでval
にも代入可能で、かつ評価すると値を返却する(これも意識してなかった。。)。
なので以下のようにval
にif
式を入れてやることも可能
val likelyCharactersSet = if (alphabet.length == 26) "english" else "not english" println(likelyCharactersSet)
実行結果は以下のようにif
式の条件が成立した場合の値がval
に入る。
english likelyCharactersSet: String = "english"
因みに結果が2つ表示されているが、これはコードのprintln
の実行結果とjupyter notebook実行時に変数の中身が表示された結果によるもの。
メソッド(関数)
ここも以前にみているのでポイントだけ抑えていく。なおこのBootcampではメソッドを関数と呼称していくとのことなので以降のBootcampの記事でもそれに倣うことにする。
- 関数のキーワードは
def
- 引数は
,
で区切ってリストにして渡す。各引数は引数名: 型
が基本でオプションでデフォルト値を設定可能 - 戻り値の方は明示したほうがベター:
def foo(x: Int): Int = println(x))
シンプルな宣言
以下はBootcampのサンプルとその実行結果
// Simple scaling function with an input argument, e.g., times2(3) returns 6 // Curly braces can be omitted for short one-line functions. def times2(x: Int): Int = 2 * x // More complicated function def distance(x: Int, y: Int, returnPositive: Boolean): Int = { val xy = x * y if (returnPositive) xy.abs else -xy.abs }
- 実行結果
defined function times2 defined function distance
実行結果が上記のようになるのはjupyter-notebook上で関数の定義のみを実行しているから。
関数のオーバーロード
この話はちゃんとは取り扱っていないので少し詳細に見ていく。以下はBootcampの記述を適当に訳したもの。
同名の関数は単一の方法だけでなくそれ以上のものとして使うことが出来る。パラメータとそのパラメータの型によってその関数のシグネチャーが決まり、それがあるためにコンパイラは同名の関数があった場合でもどのバージョンの関数を呼ぶべきであるかを判断出来る。ただ、オーバーロード関数は避けるべきだ。
紹介はしたけど使わないほうがいいって。。なら書かなくても良かった気も。
サンプルとしては以下のように同名の引数の型が異なるtimes2
関数が定義されており、それに別の型を引数として与えることでエラーなく実行できるというサンプルになっている。
この話はC++の関数のオーバーロードと同等と考えて良さそう。
// Overloaded function def times2(x: Int): Int = 2 * x def times2(x: String): Int = 2 * x.toInt times2(5) times2("7")
- 実行結果
defined function times2 defined function times2 res5_2: Int = 10 res5_3: Int = 14
2つ目のString
が引数のtimes2
関数では内部の処理にてStinrg
型がInt
型に変換された後、2倍されておりどちらの関数の実行結果においても処理後の値はInt
型になっていることがわかる。
再起関数とネストされた関数
そのまんま再起関数とネストされた関数がトピックとして記載されている。ここで重要なのは以下だろう。
- 日本語だと中括弧でいいのか??(英語だとCurly braces)
{}
でコードのスコープを定義できる - 一つの関数のスコープ内では他の関数や再起関数の呼び出しが可能
- とあるスコープ内で定義された関数はそのスコープ内でのみアクセス可能となる
再起関数については、他のプログラミング言語と同様の扱い。関数内での別関数定義はpythonとかでもサポートされているがそれと同様の扱いと考えて良さそう。
以下がBootcamp内のサンプル。
/** Prints a triangle made of "X"s * This is another style of comment */ def asciiTriangle(rows: Int) { // This is cute: multiplying "X" makes a string with many copies of "X" def printRow(columns: Int): Unit = println("X" * columns) if(rows > 0) { printRow(rows) asciiTriangle(rows - 1) // Here is the recursive call } } // printRow(1) // This would not work, since we're calling printRow outside its scope asciiTriangle(6)
- 実行結果
XXXXXX XXXXX XXXX XXX XX X defined function asciiTriangle
まあ、割と見たまんまのサンプルで、asciiTriangle
内で定義されたprintRow
という1行を出力する関数を使ってascii文字X
の三角形を出力するというもの。関数内でasciiTriangle
関数を再帰的に呼び出しており、この項の2つの要素を1つのサンプルで確認することが出来る。
基本的なことのみではあるが、ここまででもそれなりのボリュームになったので、今日はここまで。
また日を改めて残りの内容を確認していく。