SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

今からでも遅くない これから始めるScala

今からでも遅くない これから始めるScala(前編)

Scalaってどんな言語?


  • X ポスト
  • このエントリーをはてなブックマークに追加

コレクションの利用 - Listクラス

 サンプルプログラムでは、人物のリストを次のように作成しました。

[リスト8]人物のリスト
// 人物のリスト
val list = List( Male("Miles", 32), Female("Ella",18 ),
  Female("Sarah",25), Male("Wes",16) )

 先ほど定義したMaleクラスとFemaleクラスを要素としてもつListを作成して、4つのPersonオブジェクトを変数listに代入しています。変数Listの型は省略されていますが、型推論で推論されます。

 Scalaでは、組み込みのコレクション型としてArray(配列),List(LinkedList),Set(集合),Map(連想配列)などが用意されています。

 ここではListを利用しています。

 Javaの場合では、Listを利用する場合はjava.util.ArrayListなどのインスタンスを生成してから、addメソッドなどで要素を追加していきますが、ScalaではList(要素1,要素2,...)のようにListの作成を行います。同様のシンタックスシュガーはArray,Set,Mapなどにも用意されていますが、これはScalaの文法上で特別扱いされている訳ではなく、List「オブジェクト」やMap「オブジェクト」などがもつファクトリーメソッドなのです。これらはコンパニオンオブジェクトと呼ばれますが、解説は次回以降に行います。

 通常、作成されるListはイミュータブルなListになります。つまり、一度作成されたListは要素の追加/削除/変更を行うことができません。しかし、Scalaのコレクションライブラリには変更可能なコレクションも用意されており、必要に応じて切り替えて利用することができるようになっています。

 なぜ、デフォルトのListがイミュータブルになっているのでしょうか?

 「ケースクラス」もイミュータブルでした。Scalaには、「イミュータブルなデータ構造を利用することで副作用を抑えた、堅牢なプログラムを書くことができる」という思想が反映されています。

 変更可能な変数を利用すると、例えばnullなど意図しないデータが変数に代入されることで発生するバグを回避することができます。

 もちろん、イミュータブルなオブジェクトを利用することで、パフォーマンス面で不利になる局面もありえます。ですので、Scalaではデフォルトではイミュータブルですが、変更可能なデータ構造も利用できる設計になっているのです。

 Scalaにおけるコレクションの利用については、本連載の中で詳しく紹介する予定です。

関数の定義 - defによる宣言と関数リテラル・無名関数

 次は、関数を見てみましょう。サンプルプログラムで、引数の年齢以上のものを抽出する関数filterAgeを作成しました。リスト9はfilterAge関数の定義です。

[リスト9]引数の年齢以上のものを抽出する関数
// 人物のリストから、引数の年齢以上のものを抽出する関数
def filterAge( n:Int, xs:List[Person] ) = xs.filter{ _.age >= n }

 関数は、defキーワードで定義することになっていました。詳しく見てみましょう。

 まず、HelloWorldで定義したmain関数と異なるのは「{}」がないことでしょう。Scalaでは、関数本体が一行の式で定義できる場合は、「{}」を省略できます。

 また、関数の定義では、引数の宣言の後に、「:結果型」で関数の結果型を指定する必要がありましたが、filterAge関数では結果型が指定されていません。これは、変数と同様に関数の結果型も型推論が働くので省略してよいことになっています。対して、引数の型は省略することができません。

 fiterAge関数は、省略せずに次のように書いても同じことです。

[リスト10]引数の年齢以上のものを抽出する関数
def filterAge( n:Int, xs:List[Person] ):List[Person] = {
  xs.filter{ _.age >= n }
}

 さて、「=」の右側の関数本体を詳しく見てみます。List[Person]型の引数xsに対して、fitler関数を呼び出していますね。filter関数は、ScalaのListクラスに用意されたAPIで、条件に一致する要素のみをListの中身から抽出したListを返す関数です。

 問題は、このfilter関数に渡してる「{ _.age >= n }」の部分です。これは、いったい何を渡してるのでしょうか?

 結論から言うと、filter関数には「Person型の変数を受け取って、ageフィールドがfilterAge関数の引数n以上だったらtrueを返す無名関数」を渡しているのです。

 つまり、関数オブジェクトをその場で生成して、filter関数に渡しているということです。

 このように、Scalaではその場で名前をつけずに関数オブジェクトを作り出す関数リテラルという記法があり、ここではそれを利用しているのです。具体的にはリスト11のような関数をその場で定義しているイメージです。

[リスト11]無名関数を実際の関数宣言に置き換えたイメージ
def checkAge( n:Int,p:Person ):Boolean = { p.age >= n }

 「{ _.age >= n }」の「_」の部分には、List[Person]型の引数xsの個々の要素であるPerson型のオブジェクトが渡されます。「_」で渡されたPersonオブジェクトのageプロパティがn以上か、演算子「>=」を利用して判定してるのです(正確には、「>=」は演算子ではなくメソッドなのですが、分かりやすく説明するために演算子と言っています)。

 関数の返り値は、最後に評価された式の値になるので、比較した結果のBoolean型が返り値になります。

 関数リテラルの記法ですが、「{ _.age >= n }」という書き方は、実はかなり省略された書き方です。関数リテラルの書き方には何種類かあるのですが、この場合に省略せずに書くとすると、次のようになります。

[リスト12]省略しない場合の無名関数
{(p:Person) => p.age >= n }

 関数リテラルでは「(引数1:型, 引数2:型, ・・・) => { 処理 }」という書き方をします。リスト12の例では、Person型の引数pをもらうので(p:Person)と書いて、「=>」のあとに関数本体を記述しています。

 この引数の型の部分は、関数リテラルが定義できる場所でScalaが引数の型を省略できる場合は、書かなくてもよいことになっています。この場合では、 「p => { p.age >= n }」と書いてもpの引数はPerson型であることをScalaが推論してくれるので、問題ありません。

 さらに、この引数p自体も省略してしまうことが可能です。「{ _.age >= n }」のように、「_」の部分を引数として受け取るように関数を書くことができます。この「_」はプレースホルダーと呼ばれています。

 「_」は、関数リテラルの中で一度しか利用できないという制限がありますが、簡単な処理であれば「_」で済ませてしまった方が記述が単純になるというメリットがあります。

 まとめると、Listクラスのfilter関数は、「Person型の変数を一つとり、Boolean型の結果を返す関数」を引数に取る関数です。filter関数に、「年齢を比較する関数」を渡して、比較結果がtrueのPersonオブジェクトだけを集めたListが、filterAge関数の返り値になるということです。

 Listの中から条件にあった要素を抽出するような処理は、手続き型言語ではforループなどを利用して書くことが多いでしょうが、Scalaではこのようにコレクションがサポートする関数と無名関数を利用して、ループなしで書くことができます。このようなスタイルが、関数型言語の要素を取り入れているScalaの特徴の一つであると言えるでしょう。

次のページ
まとめ

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
今からでも遅くない これから始めるScala連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 尾崎 智仁(オザキ トモヒト)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5193 2010/06/23 11:21

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング