読者です 読者をやめる 読者になる 読者になる

Scala入門者向けハンズオンに参加した #ScalaNyumon

1週間前ですが・・・
6月5日(土)はScala入門者向けハンズオン@東京に行くことが出来たー!

講師は@gakuzzzzさん!
資料は@mike_neckさんの!
github.com

豪華٩(๑❛ᴗ❛๑)۶

いっぱい知見を得たのでまとめた!(何故かうまくembedできない…)
http://togetter.com/li/986758/togetter.com

最初にScalaについてご説明があって、そのあとは簡単なチケット管理システムを作るハンズオン。


ということでブログにはハンズオンのことをさらっと書いておきたい。

こんな流れ↓で進めてくださり、最後、チケットの検索や更新をするTicketRepoに自力でメソッドを追加してみよう〜というものだった!
カリキュラム · mike-neck/scala-study Wiki · GitHub
メソッドの仕様もここにあります。それにしても資料、丁寧・・・

私もTicketRepoの中身を書いてみたものの怪しい実装だらけになったので、
みけさんの実装と比べてみようと思う。

※私のはチケットをつっこむMapの名前がticketsで、みけさんのはmapなので以下のコードたちもそこの違いはあり〼

findAll: Seq[Ticket]

  • 🐳こう書いてみた。
def findAll: Seq[Ticket] = {
  tickets.values.toSeq
}
  • 🐱IDでソートしてあった・・・親切・・・
def findAll(): Seq[Ticket] = {
  map.values.toSeq.sortBy(_.id)
}

findById(id: TicketId): Option[Ticket]

  • 🐳🐱同じだった。
def findById(id: TicketId): Option[Ticket] = {
  tickets.get(id)
}

createIssue(title: String): Issue, createBug(title: String): Bug

createIssueは割愛

  • 🐳自信なさげにこう書いた。currentIdという怪しい変数、最悪っぽい…。
def createIssue(title: String): Issue = {
  val newIssue = new Issue(currentId, title)
  tickets = tickets + (currentId -> newIssue)
  currentId += 1
  newIssue
}
  • 🐱こうやればcurrentIdいらないね。あとIssuecase classなのでnewしなくてよかった、あちゃ。
def createIssue(title: String): Issue = {
  val nextId: TicketId = if (map.isEmpty) 0 else map.keys.max + 1
  val issue: Issue = Issue(nextId, title)
  map = map  + (nextId -> issue)
  issue
}

findIssuesByStatus(staus: String): Option[Seq[Issue]], findBugsByStatus(status: String): Option[Seq[Bug]]

findBugsByStatusは割愛

  • 🐳空のSeqを返す術しか分からなくて最後に苦し紛れの一文を入れた('_')ちーん
def findIssuesByStatus(status: String): Option[Seq[Issue]] = {
  val issues = tickets.collect {
    case (id, ticket: Issue) if TicketStatus.isMatchedStatus(status, ticket) => ticket
  }.toSeq
  if (issues.nonEmpty) Some(issues) else None
}

ちなみにTicketStatus.scalaにもメソッドを追加してある。

def isMatchedStatus(status: String, ticket: Ticket) = {
  status match {
    case "open" => ticket.status == TicketStatus.Open
    case "fixed" => ticket.status == TicketStatus.Fixed
    case _ => false
  }
}
  • 🐱ぎょーなんかシンプルだ。
def findIssuesByStatus(status: String): Option[Seq[Issue]] = {
  val st: Option[TicketStatus] = TicketStatus.of(status)
  st.map(s => {
    map.values.toSeq collect {
      case x: Issue if x.status == s => x
    }
  })
}

TicketStatus.scalaのメソッド〜。そもそもここの戻り値がOptionなのねそうなのね。

def of(status: String): Option[TicketStatus] = {
  status.toLowerCase match {
    case "open" => Option(Open)
    case "fixed" => Option(Fixed)
    case _ => None
  }
}

fix(id: TicketId): Boolean

  • 🐳ひどいよ〜(;▽;)else return falseなんて書きたくないよ〜(;▽;)
def fix(id: TicketId): Boolean = {
  findById(id) match {
    case Some(t: Issue) =>
      if (t.status == TicketStatus.Open)
        tickets = tickets.updated(id, new Issue(t.id, t.title, TicketStatus.Fixed))
      else return false
    case Some(t: Bug) =>
      if (t.status == TicketStatus.Open)
        tickets = tickets.updated(id, new Bug(t.id, t.title, t.description, TicketStatus.Fixed))
      else return false
    case _ => return false
  }
  true
}
  • 🐱あ。あ。existsでチェックすればいいのだ。
def fix(id: TicketId): Boolean = {
  val ticket: Option[Ticket] = map.values.find(t => t.id == id && t.status == Open)
  ticket.exists(t => {
    map = map.updated(t.id, t match {
      case x: Issue => Issue(x.id, x.title, Fixed)
      case x: Bug => Bug(x.id, x.title, x.description, Fixed)
    })
    true
  })
}

修行あるのみ✋(--)👌


最後にしょーもない感想。
今まで、既に動いてるシステムの改修しかしたことなかったので
trait作って、objectenum作って、case class作って・・・って
新規作成の流れを体験できたのはとても勉強になったー。

あとはpackage objectとかsealedとか知らなかったりよく分かってなかったりしたことも理解できてよかったなー。

学び多きイベントをありがとうございました!

*1:forkしました