ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

Chiselの勉強 - Chisel Bootcamp - Module1(1)

スポンサーリンク

前回の記事でChisel Bootcampで勉強を進めていくための準備を行い、jupyter notebook上でのScalaコードの実行を確認した。

www.tech-diningyo.info

ということで今回からは本格的にChisel Bootcampに取り組んでいく。

Module 1: Scala入門

この章の目的は以下の通り。

基本的なScalaのコードを書くことと、より応用的な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 ififに当てあはまらない場合の処理を実装できる
  • 一つ上のコードブロックでdone = trueを行っているので"we are done"が2回表示される

以前のScalaの勉強でやったとおり、if式になるのでvalにも代入可能で、かつ評価すると値を返却する(これも意識してなかった。。)。

なので以下のようにvalif式を入れてやることも可能

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つのサンプルで確認することが出来る。

基本的なことのみではあるが、ここまででもそれなりのボリュームになったので、今日はここまで。

また日を改めて残りの内容を確認していく。