「新宿Geek Lounge#5 新卒・中途研修Meetup」に参加してきた
新卒、中途のエンジニアの教育について興味があり、何か面白いイベントがないかと思っていたところ、セプテーニオリジナルさんが以下のイベントを開催していたので、参加してきました。
shinjuku-geek-lounge.connpass.com
ビズリーチ
- 佐藤さん
- 新卒2年目、普段はScala, Typescript
研修について
研修の概要
- 完全オリジナル、外注はしない
- 現場の社員が兼業で行う(2〜3年目のエンジニア)
- 4 − 5月:座学
- 6月: HTTP Server
- 7月: Web application
研修の目的
- 新卒エンジニアにどうなってほしいか?
- 求めてるのは即戦力?
- 即戦力ではなく、成長角度の高い骨太エンジニア
- 主体的
- 正しく情報収集できる
- 課題の背景、目的をふみこめる
- 0 -> 10でプロダクト作りきれる
- なぜ骨太エンジニア?
- 多事業化&事業の成長
- 事業の変化に対応しながら自分で成長できるようになってほしい
3つの研修
- 座学
- 現役エンジニアが講義(外注しない)
- 座学の目的
- 土台作り。広く浅く引き出しを増やす
- コンテンツは毎年変わる
- HTTPサーバー研修
- Webアプリクローン
- クローン元と違うとこを一個作る
- 目的
- 0 -> 10で作りきる
- 現場では0を知り辛い
やってみての課題
- 座学のモチベをキープするのが難しい
- レビューコストがかかる
- 来る人のレベル感がわからない
ドワンゴ
- 水島さん
新卒研修について
研修の概要
Scala研修のポイント
- 基本的には自習ベース
- 新卒エンジニアの理解の速さの差は大きい
- こちらのペースで作ると調整が難しい
- 1日ごとの到達目安は置く
- 強い人の質問もほぼ全て返せるエンジニアがいる
- 強い新卒も飽きさせないのが大切
- グループディスカッション
- 研修中固定で4〜5人のグループ
- その日の学びを発表
- 研修生同士で教えあう時間を作る
- 研修生が質問しやすい時間を確保する
- 講師側にとって進捗の確認に役立つ
- グループ発表
- 誤解があれば訂正する
- 日報
- 進捗確認や教え方、テキスト不備などの気づき
- 翌日以降に適宜反映
- コードを書く力について
- テキストの課題を解く
- 毎日のディスカッションや発表でアウトプットする時間を作る
- 後半のPlayの研修でプルリクを投げてレビューするスタイルなので、コードを書く練習はそっちで担保している
セプテーニ
- 池田さん
- インフラ -> アプリ
新卒研修について
研修全体のポイント
- 学ぶべき領域をはっきりさせたことがよかった
- 技術、ヒューマンスキルどちらも
- ビズリーチ同様骨太エンジニアの育成が目的
Scala研修について
- Scala textを一巡(15日)
- REPLで確認
- 理解できるまで質問、調査
- 練習問題を準備
- 答えを教えるのではなくプロセスを通じて学んでもらう
- ドワンゴさんのPlayテキストでのウェブアプリ研修
- Webアプリをチーム開発
- DDDによるアプリの作り直し
アイスタイル
Before
- 中途向け技術研修がなかった結果、個人のモチベ依存になり、できるひとは転職していき、できない人が残るようになってしまった。
After
- ナレッジの一元化
- なんでも答えるチャンネルを作成(Go答えるマン)
- 社外向け勉強会
- アウトプット自体の勉強会
- なぜアウトプットを出すと良いか、勉強会でアウトプットすると良いか
感想
- ビズリーチさんは研修の目的とゴールがはっきりしていると感じた
- ビズリーチさんはドワンゴさんと比べると専任のメンターや講師がいないのでフォローやレビューなどが負担になるの
で、きめ細かいフォローはできなさそう
- ただ、教えることで学びになるのでそれを狙っているのかもしれない
- ドワンゴさんの研修は新しいこと言語に触れることが研修の目的と言っていたのでプログラミングができる新卒向けだと感じた。
Scalaに強い講師やメンターが多いので技術的に強い新卒の満足度は高いかもしれない
- ただ未経験の新卒などを教育するには数ヶ月の研修期間では到底足りないのでゴールを明確にした上で必要なカリキュラムを用意する必要があると感じた
- セプテーニさんは3月中盤から準備したらしく、準備が足りてないところがあったと話していたので、準備がやはり大切
- プレゼンの構成力、完成度でもビズリーチさんが一番良かった
- 質問も一番きていた
- おそらく研修の内容の紹介に入る前に前提条件や研修のゴールなどをしっかり話していたからこと聞き手と話し手の溝があまりなかったのがよかった
- どの会社の方も、人によって進行度や理解度が異なるのでできる人を飽きさせないようにしつつ、進行が遅い人をサポートするのに苦労しているようだった
- 中途の教育に関しては、最低限必要な知識を研修などでサポートしつつ、OJTで教育していくスタイルが多かった
「Scalaスケーラブルプログラミング」で気になったところまとめ ~ 4章&5章 ~
この記事は?
- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレス
- 発売日: 2016/09/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
第4章 クラスとオブジェクト
クラス定義は基本的にJavaと同じ
Scalaでは以下のようにクラスを定義する
class ChecksumAccumulator { // ここにクラス定義を書く }
オブジェクトを作成するときはnew
を使う
val ca = new ChecksumAccumulator
val
で作成したオブジェクトに別のオブジェクトを代入することはできない
scala> val hoge = List("hoge","fuga") hoge: List[String] = List(hoge, fuga) scala> hoge = List("foo", "bar") <console>:8: error: reassignment to val hoge = List("foo", "bar") ^
Scalaでは明示的にアクセス修飾子を指定しなければメンバーはpublic
になる
アクセス修飾子をprivate
にした場合
// 例で使用するクラス class ChecksumAccumulator { private var sum = 0 }
scala> val ca = new ChecksumAccumulator ca: ChecksumAccumulator = ChecksumAccumulator@60dd0587 scala> ca.sum = 5 // privateなのでアクセスできない <console>:12: error: variable sum in class ChecksumAccumulator cannot be accessed in ChecksumAccumulator ca.sum = 5 ^ <console>:13: error: variable sum in class ChecksumAccumulator cannot be accessed in ChecksumAccumulator val $ires0 = ca.sum ^
アクセス修飾子を指定しなかった場合
// 例で使用するクラス class ChecksumAccumulator { var sum = 0 }
scala> val ca = new ChecksumAccumulator ca: ChecksumAccumulator = ChecksumAccumulator@60dd0587 scala> ca.sum = 5 // アクセス修飾子を指定していない(public)なのでアクセス可能 ca.sum: Int = 5
メソッドのパラメータはval
である
パラメータがval
なのは、val
の方が推論に便利だかららしい。(p.081)
// 例 class ChecksumAccumulator { private var sum = 0 def add(b: Byte): Unit = { b = 1 // bはvalなので代入できない。コンパイルエラーになる。 sum += b } }
Scalaにはシングルトンオブジェクトと呼ばれるものがある
Scalaではクラスが静的メンバー(static member)を持つことができない。その代わりにシングルトンオブジェクトと呼ばれるものを持つことができる。
シングルトンオブジェクトはclass
キーワードの代わりにobject
キーワードを使う。
object ChecksumAccumulator { // ここに定義を書く }
クラスとシングルトンオブジェクトが同じ名前を持つ時、そのクラスのコンパニオンオブジェクトと呼ぶ。
逆にクラスはそのオブジェクトのコンパニオンクラスと呼ぶ。
ちなみにコンパニオンクラスと同じ名前ではないシングルトンオブジェクトをスタンドアロンオブジェクトと呼ぶ。
クラスとそのコンパニオンオブジェクトはお互いのprivateメンバーにアクセスできる
class Hoge { private val name = "hoge" } object Hoge { def printHogeName(): Unit = { val hoge = new Hoge println(hoge.name) // クラスとオブジェクトが同じ名前なのでprivateメンバーにアクセスできる } }
class Hoge { private val name = "hoge" } object Fuga { // クラスとオブジェクトが同じ名前ではないのでエラーになる def printHogeName(): Unit = { val hoge = new Hoge println(hoge.name) } }
ちなみにクラスとオブジェクトが同じ名前じゃない場合は以下のようなエラーになります
// 上の例の場合 <console>:18: error: value name in class Hoge cannot be accessed in Hoge println(hoge.name)
Scalaプログラムを実行する方法
Scalaプログラムを実行するにはmain
メソッドを持つスタンドアロンオブジェクトを作る必要がある。
object Hoge { def main(args: Array[String]) = { for (arg <- args) println("arg = " + arg) } }
コンパイルを行うにはscalac
コマンドを使用する。
実行にはscala
コマンドを使用する。
$ scalac Hoge.scala $ scala Hoge foo bar baz arg = foo arg = bar arg = baz
Appトレイトを使うとmain
メソッドを省略できる
// Appトレイトを使うとmainメソッドを省略できる object Hoge extends App { for (arg <- args) println("arg = " + arg) }
第5章 基本型と演算子
※基本的にJavaと変わらないため、Javaとの違いを中心に書いています。
ダブルクォートを3個続けると生の文字列を表現できる
特殊文字を文字列に含めたい場合、普通はエスケープしないといけないが、ダブルクォートを3つ使うと特殊文字をエスケープしなくても出力などが可能になる。
// 通常の出力 scala> println("\\\"\'\\n\\t\\r") \"'\n\t\r // ダブルクォートを3つ使った場合 scala> println("""\"'\n\t\r""") \"'\n\t\r
Scalaではシンボルリテラルを作ることができる
シンボルリテラルを作るには'シンボル名
のように'
をシンボル名の前に入れる。
同じシンボル名はすべて同じSymbolオブジェクトを参照するので少し注意が必要。
scala> val s = 'aSymbol
s: Symbol = 'aSymbol
Scalaでは文字列リテラルを加工して式に含めることができる
文字列の前にs
を、文字列中に$
を入れることで文字列に式を含めることができる
scala> s"The answer is ${6 * 7}" res5: String = The answer is 42 scala> val name = "Luke" name: String = Luke scala> s"Use the Force $name" res6: String = Use the Force Luke
f
を使用すると書式を指定できる
// 書式を指定しない場合 scala> s"${math.Pi}" res10: String = 3.141592653589793 // 書式を指定した場合 scala> f"${math.Pi}%.5f" res11: String = 3.14159
Scalaでは演算子はメソッドである
Scalaでは+
や-
といったメソッドだけでなくすべてのメソッドを演算子のように使うことができる
scala> val sum = 1 + 2 // 1.+(2)を呼び出している sum: Int = 3 scala> val sumMore = 1.+(2) sumMore: Int = 3 scala> val s = "Hello, World!" s: String = Hello, World! scala> s indexOf 'o' // s.indexOf('o')を呼び出している res0: Int = 4 scala> -2.0 res1: Double = -2.0 scala> (2.0).unary_- //前置、後置演算子の場合はunary_[演算子]を呼んでいる res2: Double = -2.0
またScalaの慣習ではメソッドが副作用を持つ場合()
をつけ、副作用を持たない場合は()
を省略する
scala> val s = "Hello, World!" s: String = Hello, World! // toLowerCaseには副作用がないので()はつけない scala> s.toLowerCase res3: String = hello, world!
オブジェクトが等価どうかを比較する場合は==
を使う
Scalaの==
はJavaのequals
と同じで、
Scalaのeq
はJavaの==
と同じ
scala> List(1,2,3) == List(1,2,3) res6: Boolean = true scala> List(1,2,3) eq List(1,2,3) res7: Boolean = false
「現場で役立つシステム設計の原則」まとめ ~ 2章 ~
システム設計の原則知ってますか?
この記事は「現場で役立つシステム設計の原則」を読んで勉強になったことや仕事で役に立ちそうなことをまとめた記事です。 あくまでまとめで、説明できなさそうなところは省いているのできちんと理解したい人は本を買って読んでください!
現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法
- 作者: 増田亨
- 出版社/メーカー: 技術評論社
- 発売日: 2017/07/05
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
1章のまとめはこちら「現場で役立つシステム設計の原則」まとめ ~ 1章 ~
場合分けのロジックを整理する
区分や種別がコードを複雑にする
顧客区分・料金種別・商品分類のように区分や分類を書き分ける基本手段がif文やswitch文だが、プログラムにいろいろな箇所に同じようなif/switch文が重複すると、区分追加や区分の変更の際にプログラムの変更がやりにくくなる。
判断や処理のロジックをメソッドに独立させる
1章のオブジェクト指向の考え方に基づき以下のようにコードを整理する。
- コードの塊は、メソッドとして抽出して独立させる
- 関連するデータとロジックは、1つのクラスにまとめる
// if文の中にロジックがあるのでBad if(customerType.equals("child")) { fee = baseFee * 0.5; } // メソッドに抽出されているのでGood if (isChild()) { fee = childFee(); } // メソッド定義は省略
else句をなくすと条件分岐が単純になる
else句はプログラムを複雑にするので、else句をできるだけ書かないようにするとプログラムが単純になる。
ローカル変数に結果を持たせreturnするのではなく、条件が一致した時にreturnするようにすればコードがシンプルになる。これを早期リターンという。
またelse句を使わずに早期リターンをする書き方をガード節と呼ぶ。
// Bad Yen fee() { Yen result; if(isChild()) { result = childFee(); } else if(isSenior()){ result = seniorFee(); } else { result = adultFee(); } return result; } // 早期リターンを使うとこうなる Yen fee() { if(isChild()) { return childFee(); } else if (isSenior()) { return seniorFee(); } else { return adultFee(); } } // 早期リターンを使うことでelse句をなくすことができる(ガード節) Yen fee() { if(isChild()) { return childFee(); } if(isSenior) { return seniorFee(); } return adultFee(); }
区分ごとのロジックを別クラスに分ける
区分に関するロジックを区分ごとのクラスに分けて記述することで、区分ごとにロジックが整理されるので、どのにロジックがあるかが明確になる。
// 大人料金クラス class AdultFee { Yen fee() { return new Yen(100); } String label() { return "大人"; } } // 子供料金クラス class childFee() { Yen fee() { return new Yen(50); } String label() { return "子供"; } } // シニア料金も同様にクラスに分ける
インターフェースを使って区分ごとのクラスを同じ「型」として扱う
区分ごとにクラスにわけるとロジックはわかりやすくなるが、呼び出し側でどのクラスを呼ぶかを意識しなければならなくなる。
この問題を解決するために区分ごとに分けたクラスを同じ型として扱える様にインターフェースを定義すると異なるクラスを同じ型として扱うことができる。
interface Fee { Yen yen(); String label(); } class AdultFee implements Fee { Yen yen() { return new Yen(100); } String label() { return "大人"; } } // ChildFee,SeniorFeeクラスも同様にFeeインターフェースを実装する
インターフェースを使うと呼び出す側のコードはif文を使うことなく区分を分けることができる
Fee fee; Charge(Fee fee) { this.fee = fee; // Feeインターフェースを実装しているAdultFee型, ChildFee型, SeniorFee型のどれでも良い } Yen yen() { return fee.yen(); } }
この様にインターフェースと区分ごとの専用クラスを組み合わせて、区分ごとの異なるクラスのオブジェクトを「同じ型」として扱う仕組みを多態という。
列挙型(enum)を使うと区分ごとの一覧を分かりやすくできる
多態は区分ごとのロジックを整理する上では便利だが、一覧が分かりにくくなるという問題がある。
しかしJavaの列挙型(enum)を使うと区分ごとの一覧を明示的に記述できる。
enum FeeType { adult(new AdultFee()), child(new ChildFee()), senior(new SeniorFee()); private Fee fee; private FeeType(Fee fee) { this.fee = fee; } Yen yen() { return fee.yen(); } } // 区分名から料金を取得する Yen feeFor(String feeTypeName) { FeeType feeType = FeeType.velueOf(feeTypeName); return feeType.yen(); }
上記の様に多態と列挙型を組み合わせることでif文を使わずに異なる区分の料金を取得することができる。
なお列挙型を使って、区分ごとのロジックを分かりやすく整理する方法を区分オブジェクトと呼ぶ。
[書評] ポジティブシンキングでは目標達成できない!? ~「残酷すぎる成功法則」を読んで ~
どんな本か?
- 作者: エリック・バーカー,橘玲
- 出版社/メーカー: 飛鳥新社
- 発売日: 2017/10/25
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
自己啓発本が結構好きなので、数はたくさん読んできたのですが、 中には本当かな?と思ってしまうようなものもありました。
ただ数ある自己啓発本の中でも橘玲さんが書いた『幸福の「資本」論――あなたの未来を決める「3つの資本」と「8つの人生パターン」』と言う本はいわゆるただの「思考を変えればうまくいく!」とか「成功できるたった一つの法則」みたいな怪しい類のものではなく、人生における幸せを資本と言う切り口で書いた作品で個人的にとても納得がいく本でした。
幸福の「資本」論――あなたの未来を決める「3つの資本」と「8つの人生パターン」
- 作者: 橘玲
- 出版社/メーカー: ダイヤモンド社
- 発売日: 2017/06/16
- メディア: Kindle版
- この商品を含むブログ (3件) を見る
その橘さんが監訳を務めたのがこの「残酷すぎる成功法則」と言う本です。
この本によると、今までの自己啓発本は2つのパターンに分かれています。
- 私はこの方法で成功しました。あなたもこれを使えば成功できます系
- 歴史的な偉人はこう言っている。もしくは偉人はこう決断した系
上記のような今までの自己啓発本をある種批判する形で書かれたのがこの本で、「科学的なエビデンスに基づいて主張しましょうよ」というテーマのもと人の成功とか幸せについて書かれています。
ポジティブ思考では目標を達成できない?
例えば、アメリカ海軍の厳しい訓練に合格する人と途中で脱落する人の違いは「ポジティブな心のつぶやき」があるかということが海軍の調査でわかったことが本書では挙げられています。
海軍の調査で、グリッドを持った人びとが逆境に耐える際に行っている(ときに無意識に)いくつかのことが明らかになった。そのなかに、心理学的調査で何度も浮かびあがった一つの習性があった。
それは「ポジティブな心のつぶやき」だった。
では、常にポジティブでいればいいかというとそういうことでもなく、ポジティブな思考はときに目標を遠ざけてしまうことも本書で挙げられています。
ニューヨーク大学心理学教授のガブリエル・エッティンゲンは、欲しいものを夢に思い描くだけで、実現の可能性が高まるといった類の説に猜疑的だった。
(中略)
あなたは、ダイエット後のほっそりした水着姿を思い描いたりするだろうか? ある実験で、そんな風にポジティブに思い描いた女性たちは、ネガティブなイメージを浮かべた女性たちに比べて、体重の減少分が一〇キロほど少なかったという。完璧に理想通りの仕事に就くことを夢見ているなら、出願書類を出す数が自然と減り、その結果、内定をもらえる数も減ることになる。成績でたくさんAをもらうことをイメージしている者は、勉強時間が減り、成績が落ちることになる。
つまり、ポジティブ思考には有効に機能する場合と逆に弊害となる場合があるということです。 ポジティブ思考だけでなく本書ではこのような一般的な自己啓発本で言われている思考法や成功哲学について、その効果を検証し、その効果と弊害を明らかにしています。
現実を変えるにはどうすればいい?
ポジティブ思考が目標達成に悪影響を及ぼすなら、目標を達成するのにどうすればいいのでしょう?
ニューヨーク大学の心理学者、ピーター・ゴルヴィツァーとヴェロニカ・ブランドスタッターの研究によれば、たとえば、目標を達成するための行動をいつ、どこで、どのように取るかなど、ざっくりと計画しているだけで、学生たちが目標を実現できる確率が四〇%上がったという。
二つの魔法の言葉は、「もしも(If)」と「そのときは(Then)」である。予見できるどんな障害に対しても、「もしXが起きたら、Yをすることで対処しよう」と考えておくだけで、結果は大違いだ。
実はあらかじめ計画をしておくこと、それも、もしものことを考えて計画をたてておくことが大事だと述べられています。 これをより実践しやすい形にまとめたものがWOOPと呼ばれるものです。 WOOPとは以下の頭文字をとったもので、仕事、人間関係、ダイエットなどあらゆる目標に対して適用できるものです。
- 願い(Wish)
- 成果(Outcome)
- 障害(Obstacle)
- 計画(Plan)
しかしこのWOOPはあまりにも現実可能性がないものには効かない(「明日までに20キロ痩せる・・・など」)とのこと。
何か目標がある場合、まずこのWOOPを考えてみることが目標を達成させる近道かもしれません。
つまるとこ何が大事なの?
本書では「自分を知ること」と「調整すること」が大切だと説いています。
成功とは、一つだけの特性の成果ではない。それは、「自分はどんな人間か」と「どんな人間を目指したいか」の二つを加味しつつ、そのバランスを調整することだ。
自己啓発本で言われていることや世の成功法則というものには必ず良い面と悪い面があります。まずはそれらについて科学的なエビデンスを元に理解し、自分が進みたい方向に進んでいけるようにうまいこと利用しながら調整していく(バランスをとっていく)ことがいいのでしょう。
読んでみての感想
普段、自己啓発本を読むときはひどく落ち込んでいたり、逆に意識が高すぎる時が多く、内容の正しさよりも読むことで感化されることを目的としてしまうことが多かったのですが、エビデンスベースで展開されると変に心が揺れ動くことが無いので、逆に成功や幸せというものをどう実現か冷静に考えることができたのがこの本を読んで一番良かったことかもしれません。
ちなみに
橘さんの公式サイトに序文と解説が載っていました。
タダなのでぜひ読んでみてください。
「Scalaスケーラブルプログラミング」で気になったところまとめ ~ 2章&3章 ~
この記事は?
- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレス
- 発売日: 2016/09/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
第2章 Scalaプログラミングの第一歩
変数は2種類ある
var
は何度でも代入可能な変数。
val
は初期化後別の値を代入できない変数。
関数定義の仕方
基本的には以下のように定義するが、省略可能なものもある。 関数が1文のみの場合中カッコは省略できる。
Javaではメソッドが返す値の型を「戻り値(return type)」と言うが、Scalaでは「結果型(result type)」と呼ぶ。
def max(x: Int, y: Int): Int = { if (x > y) x else y }
関数が意味のある値を返さない場合Unit型が返る
Unit型はJavaのvoidに似ており、Javaのvoidを返すメソッドはUnitを返すメソッドにマッピングされる。
scala> def greet() = println("Hello, World!") greet: ()Unit
配列の要素にアクセスするときは()
を使う
Javaでは配列の要素にアクセスする場合は[]
を使用するがScalaでは()
を使用する
// これはJava String hoge = args[0] // これはScala val hoge = args(0)
while
とif
は基本的にJavaと同じ
以下のようにScalaのwhile
とif
は基本的にJavaと同じ
var i = 0 while (i < args.length) { if (i != 0) // Java同様に中カッコを省略することもできる print(" ") print(args(i)) i += 1 }
foreach
は関数リテラル、右矢印、関数本体を使う
関数リテラルの構文は以下のように関数パラメータ、右矢印、関数本体の3つからなる
(x: Int, y: Int) => x + y
下の例ではarg
が関数リテラルでprintln(arg)
が関数本体である。
// 例
args.foreach(arg => println(arg))
また関数リテラルが1個の引数をとる1文から成る場合は引数を省略できる
args.foreach(println)
for式
では<-
を使う
// 例 for (arg <- args) println(arg)
上の例のarg
は必ずval
になる。
<-
は「in」と読むと理解しやすいかもしれない。
for (arg in args)
第3章 Scalaプログラミングの次の一歩
Scalaではnew
を使ってインスタンスを生成できる
インスタンスを生成する際に型を指定することもできる
val hoge = new Array[String](3)
Scalaではすべての演算がメソッド呼び出し
1 + 2
は(1).+(2)
と同じで1
の+
メソッドを呼び出し引数に2
を渡している
配列の要素アクセスや初期化も同様にメソッドを呼び出している
// 要素アクセス hoge(0) と hoge.apply(0) は同じ hoge(0) = "piyo" と hoge.update(0, "piyo") は同じ // 初期化 val numNames = Array("zero", "one", "two") は val numNames = Array.apply("zero", "one", "two") と同じ
ScalaのListはイミュータブルである
リストの内容を変更するようなメソッドを呼ぶと、新しい値を持つ新しいリストが作成され、返される。
例えば::
はリストの先頭に新しい要素を追加して得られるリストを返す。
scala> val oneTwoThree = 1 :: 2 :: 3 :: Nil oneTwoThree: List[Int] = List(1, 2, 3)
ちなみにListを初期化する場合は最後にNil
をつける必要がある。
scala> val oneTwoThreeFour = 1 :: 2 :: 3 :: 4 <console>:7: error: value :: is not a member of Int val oneTwoThreeFour = 1 :: 2 :: 3 :: 4 ^ scala> val oneTwoThreeFour = 1 :: 2 :: 3 :: 4 :: Nil oneTwoThreeFour: List[Int] = List(1, 2, 3, 4)
タプル(tuple)はリスト同様イミュータブルだが、異なる方の要素を持つことができる
タプルの個々の要素にアクセスするには._
と1から始まる要素の番号を指定する
scala> val messi = ("Barcelona", 10) messi: (String, Int) = (Barcelona,10) scala> messi._1 res14: String = Barcelona scala> messi._2 res15: Int = 10
集合(Set)とマップ(map)にはミュータブルなものとイミュータブルなものがある
Setはデフォルトだとイミュータブルになる
//デフォルトだとイミュータブルなSetインスタンスが返る scala> var realMadrid = Set("Ronald", "Ramos", "Isco", "Modric") realMadrid: scala.collection.immutable.Set[String] = Set(Ronald, Ramos, Isco, Modric) // 新しい要素を追加するとイミュータブルなSetの場合、新しいインスタンスを返す scala> realMadrid + "Kroos" res16: scala.collection.immutable.Set[String] = Set(Ronald, Kroos, Ramos, Modric, Isco) // 元のインスタンスの要素は変わっていない scala> realMadrid res17: scala.collection.immutable.Set[String] = Set(Ronald, Ramos, Isco, Modric)
ミュータブルなSetを使うにはimportが必要
scala> import scala.collection.mutable import scala.collection.mutable scala> val bigFour = mutable.Set("Federer", "Nadal", "Djokovic") bigFour: scala.collection.mutable.Set[String] = Set(Djokovic, Nadal, Federer) scala> bigFour += "Murray" res18: bigFour.type = Set(Djokovic, Nadal, Murray, Federer) scala> bigFour res19: scala.collection.mutable.Set[String] = Set(Djokovic, Nadal, Murray, Federer)
Mapもデフォルトはイミュータブルで、ミュータブルなMapを作るにはimportする必要がある。
scala> val artists = Map(1 -> "Suchmos", 2 -> "BUMP OF CHICKEN", 3 -> "Utada Hikaru") artists: scala.collection.immutable.Map[Int,String] = Map(1 -> Suchmos, 2 -> BUMP OF CHICKEN, 3 -> Utada Hikaru)
scala> import scala.collection.mutable import scala.collection.mutable scala> val rappers = mutable.Map(1 -> "Jay-Z", 2 -> "Nas") rappers: scala.collection.mutable.Map[Int,String] = Map(2 -> Nas, 1 -> Jay-Z) scala> rappers += (3 -> "Q-Tip") res20: rappers.type = Map(2 -> Nas, 1 -> Jay-Z, 3 -> Q-Tip) scala> rappers res21: scala.collection.mutable.Map[Int,String] = Map(2 -> Nas, 1 -> Jay-Z, 3 -> Q-Tip)
関数型のスタイルに近づくにはvar
を使わないこと
// これは命令型スタイル def printArgs(args : Array[String]): Unit = { var i = 0 while(i < args.length) { println(args.(i)) i += 1 } } // これは関数型スタイル(純粋な関数型ではない!) def printArgs(args : Array[String]): Unit = { args.foreach(println) }
純粋な関数型は副作用を持たない。結果型がUnit
になっていると副作用があることを意味している。
「Scalaスケーラブルプログラミング」で気になったところまとめ ~ 1章 ~
この記事は?
- 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
- 出版社/メーカー: インプレス
- 発売日: 2016/09/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
第1章 スケーラブルな言語
Scalaはプログラマーが修正・拡張できるように設計されている
組み込み型(Scalaの中核機能として組み込まれている型)になくても、新しい型を作ることも可能。
型同様にScalaでは新しい制御構造も作ることができる
Scala APIのAkkaのreceive
ブロックやメッセージ送信(!
)はScalaの組み込み演算ではない。
Scalaはオブジェクト指向言語である
Scalaは純粋なオブジェクト指向言語である。例えばScalaの1 + 2
はIntクラスの+
と言うメソッドを呼び出している。
Scalaにはトレイト(traits)というものがある。トレイトはJavaで言うインタフェースのようなものだが、Javaのインターフェースと違い、メソッドの実装やフィールドを持つことができる。 またクラスにトレイトのメンバーを追加することでミックスイン合成(mixin composition)というテクニックを使うこともできる。
Scalaは関数型言語である
関数型言語には2つの特徴的な発想がある。
- 関数を一人前の値(first class value)として扱うことができる。整数や文字列と同様に関数を他の引数に渡したり、処理結果として返すことができる。
- データはイミュータブル(書き換えができない)ものであるべきという発想。メソッドは副作用(side effect)を持ってはならず。参照透明(referentially transparent)((参照透明とはJavaのStringクラスの
replace
メソッドのように元のデータを書き換えない、他の作用を持たないことをいう))であるべきという発想がある。
Scalaを選ぶべき理由
- 互換性
- 簡潔さ
- 高水準の抽象
- 高度な静的型付け
互換性
ScalaはJavaとの相互運用を実現するように設計されている。 ScalaプログラムはコンパイルするとJava仮想マシンバイトコードになる。
簡潔性
極端にいうとScalaのコード量はJavaの10分の1程度に短縮できる。 その理由は2つあり、1つはJavaのように必ず書かなければいけないコードが少ないこと。もう1つはScalaには型推論があり、型情報を反復する必要がないから。という理由があげられる。
高水準の抽象
Javaではループ処理を使った冗長なコードでしか実現できないことをScalaでは述語関数などを用いて簡潔にすることができる。 例えば文字列に大文字が含まれているかを調べる処理はScalaとJavaで以下の違いがある。
// これはScala val nameHasUpperCase = name.exists(_.isUpper)
// これはJava boolean nameHasUpperCase = name.chars().anyMatch( (int ch) -> Character.isUpperCase((char) ch) );
高度な静的型付け
ScalaにはJavaのような静的型システムを持ちつつ、静的言語によくある冗長性を型推論により取り除き、柔軟性のなさをパターンマッチングと型を記述したり合成できる方法で回避している。
Scalaのルーツ
Scalaは多くのプログラミング言語の影響を受けている。 表面的にはJavaやC#の構文を採用しているが、オブジェクトモデルはSmalltalk, Rubyから、関数型プログラミングはSML, OCaml, F#に近い。 またアクターベースの並行処理ライブラリであるAkkaはErlangの影響を受けている。
「現場で役立つシステム設計の原則」まとめ ~ 1章 ~
システム設計の原則知ってますか?
この記事は「現場で役立つシステム設計の原則」を読んで勉強になったことや仕事で役に立ちそうなことをまとめた記事です。 あくまでまとめで、説明できなさそうなところは省いているのできちんと理解したい人は本を買って読んでください!
現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法
- 作者: 増田亨
- 出版社/メーカー: 技術評論社
- 発売日: 2017/07/05
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
この本を書いた方
著者は増田亨さんという方で、ドメイン駆動設計も用いたアプリケーション設計・開発をやられている方です。 スライドシェアにもドメイン駆動設計関連のスライドがたくさんありました。
第1章「小さくまとめてわかりやすくする」
ソフトウェアの変更が大変な理由
- ソフトウェアの修正や変更で副作用が出たり、予期せぬ問題が出るのは設計(=ソースコード)に問題があるから
- 変更が大変なプログラムは3つの特徴がある
- メソッドが長い(理解が大変)
- クラスが大きい(変更時の影響範囲の特定が難しくなる)
- 引数が多い(変更時の影響範囲の特定が難しくなる)
- ちょっとの修正が重なることで全体が複雑になる。
プログラムの変更を楽にする書き方
- わかりやすい名前を使う
- 略語や一致しない名前の使用を避ける
// Bad int a; int qty; return qty * up;
// Good int quantity; return quantity * unitPrice;
- コードのまとまり(処理のまとまり)ごとに空白行を入れる
- 目的ごとに変数(= 説明用の変数)を用意する(破壊的代入*1を避ける)
// Bad int price = quantity * unitPrice; // 数量x単価 if (price < 3000) price += 500; // 送料 price = price * taxRate(); // 税込み金額
// Good int basePrice = quantity * unitPrice; // 数量x単価 int shippingCost = 0; // 送料 if (basePrice < 3000) shippingCost = 500; int ItemPrice = ... // 税込み金額
- メソッドとして独立させる(メリットは以下)
- 変更の影響をメソッド内に閉じ込めやすくなる
- 詳細をメソッドに記載するので呼び出し元のコードが綺麗になる
- メソッドの名前からコードの意図を理解しやすくなる
int basePrice = quantity * unitPrice; // 数量x単価 int shippingCost = shippingConst(basePrice) // 送料計算を別のメソッドに切り出す int shippingCost(int basePrice) { ... }
- 異なるクラスの重複したコードをなくす
- 業務の関心ごとに対応したクラス(ドメインオブジェクト)を作る
- 例えば、送料に関することは送料クラスにまとめることで変更の対象や影響範囲をそのオブジェクト内に限定できる
小さなクラスでわかりやすく安全にする
- 業務で扱う値は基本データ型で扱う値と一致しないことがある
- 例えば、業務では単価は1億円まででも、intを使うとマイナス21億円からプラス21億円まで扱うことができてしまう
- 業務的に正しい値を扱えるように独自のクラス(値オブジェクト)を作成する*2ことで異常な値を防ぐことができる
- 例えば、数量に特化したQuantityクラスや電話番号に特化したTelephoneクラスを作成することで異常な値を防ぐと同時にコードの意図も明確にすることができる
- 値オブジェクトを不変にする(別の値が必要になったら別オブジェクトを作成するようにする)
- 変数の上書きは予期せぬ副作用を呼ぶ可能性があるため
- 型を使ってコードをわかりやすく安全にする
- 型を利用してコンパイラに間違いを見つけてもらう
// Bad int amount(int unitPrice , int quantity) { ... // unitPriceとquantityの渡す順番を間違えてもコンパイルエラーにならない } // Good Money amount(Money unitPrice, Quantity quantity) { ... // unitPriceとquantityの渡す順番を間違えるとコンパイルエラーになる }
1章まとめ
- コード整理の基本は名前と段落にあり
- 短いメソッド、小さなクラスでコードを整理しよう
- 値オブジェクトを使ってわかりやすく安全にしよう
- クラス名やメソッド名を業務の用語と一致させるとプログラムがわかりやすくなり、変更もやりやすくなる