
2025.03.19
ドバイ不動産投資の最前線 専門家が語る、3つの投資モデルと市場の展望
リンクをコピー
記事をブックマーク
中村学氏(以下、中村):次はfor expression、for式についてです。「式」と言うように、Scalaのforは式です。他の言語ではfor文と言われますが、Scalaのforは式です。そのため評価すると値になります。Scalaの式は大きく分けて2種類あります。予約語のyieldが付くものと、付かないものです。
yieldが付かないfor式は、基本的に他の言語のforeach構文とほぼ一緒です。全体の式としての評価結果はユニットという、何の意味ももたない値になります。yieldが付かないfor式の意味合いは、for文や他の言語のforeach文とほとんど一緒です。ここではyieldの付くfor式について、中心に見ていきたいと思います。
yieldの付くfor式をfor comprehensionと呼びます。聞いたことがある方いますか? サンプルコードがありますが、こんな感じでforとyieldを組み合わせて使います。このcomprehensionは、日本語で言うと“for内包表記”と言われたりします。
内包表記とは何かというと、内包表記の反対語として外延的記法、外延表記という表記の仕方があります。それは何かというと、集合を表現するときに、その要素の具体的な値を列挙して書いていくようなかたち。{ }を使ってカンマ区切りで具体的な値を列挙していって、その集合全体を表現するようなものを外延的記法と呼びます。
外延的記法は具体的でわかりやすいですが、無限の要素がある、例えば整数や自然数など、無限の要素がある集合を表現しようとすると、列挙しきれないなどいろいろな問題があるので、省略のようなことが使われます。そうした場合、誤解なく集合全体を表現するのが難しい場合があります。そのため、それを避けるために、内包的記法、内包表記と言われるような書き方が存在しています。
これは具体的に要素の条件を明示して、要素の集合を表すような書き方です。例えばxは整数かつx÷2の剰余は0になるというものの、x全体。それが偶数の集合になる、といったような数学の書き方があります。これを内包表記と言ったりします。
一部のプログラミング言語では、この内包表記を言語としてサポートしています。PythonやHaskellとか使われている方はご存じかもしれませんが、リスト内包表記と言って、リストの値を構築するのに、内包表記のような感じで書けます。
このx in rangeやy in rangeのようなものを書くことで、xとかyの条件を明示して、x * yでその集合全体の構成要素を表します。Haskellも、xが1から3までの間と、yが5から7までの間と。x * yが要素の構成要素なので、集合を表すような書き方ができます。数学の書き方と似ていますね。
Scalaのfor-yieldも、実はこれと同じもので、内包表記になります。Scalaのforを書いて、yieldを付けたものが他の言語で書くのとまったく同じ意味合いを持っている感じです。その要素の一個一個を表すx * yが一番左側に来るのか、それともfor式全体のyieldの後ろにあるのかで、大きく見た目が違いますが、やっている意味合いとしては同じです。
数学の書き方的なものは、そのxの要素が一番条件の左側に来るので、PythonやHaskellなどは数学に親しんでいる人はわかりやすいかと思います。余談ですが、僕なんかはけっこうxとかyとかの条件が先に左側に来て、最後にそれを使ってyield式が来るというので、個人的にはこちらのほうが好きです。
そのforの( )の中ですね。こちらにxとかyとかの条件を書いて、yieldが各要素の実際の計算になります。Scalaのfor式は( )の代わりに{ }を使えます。この{ }を使うと何がうれしいのかというと、複数の条件があったときはだいたいセミコロンで区切りますが、{ }で書くと、改行をセミコロンの代わりにできます。
条件式がxとyの2つぐらいであればそんなに変わりませんが、これが4つ、5つぐらいになると、1行で書くのはなかなかわかりづらくなっていきます。そのため、この{ }を使って改行で表すというのがよく使われます。条件が多くなってくると、こちらの{ }のほうが見やすくなります。ここまでが内包表記の一般的な説明です。
今までリストの作り方を内包表記でみてきましたが、Pythonだとリスト内包表記以外にも、生成したいオブジェクトごとに内包表記が用意されています。
例えばリスト内包表記だとさっきの[ ]ですが、セット内包表記の場合はこの{ }を使うと、全体としてセットになります。その要素の条件式や、その要素の式自体は同じですが、作られるオブジェクトがリストなのかセットなのかで分かれ、構文が用意されています。
Haskellでもリスト内包表記がありますが、HaskellにはGHCという実行環境があり、GHCには内包表記の拡張が用意されていて、その拡張を使うと、リスト以外の要素でも内包表記を使って書けます。例えば、Maybeのようなものを作りたいときに、内包表記を使えます。Scalaの場合、実はけっこうHaskellの内包表記と近いですが、基本的にmapとflatMapというメソッドを持っているオブジェクトなら、全部このfor式、内包表記で書けます。
最初にxの条件式にSeq("1", "2", "abc", "4")のようなものを用意して、yの右辺は全体的にOptionになる感じです。yのTry(x.toInt).toOptionとやると、1とか2とかはIntにできますが、abcがIntにできないので、ここがNoneになります。
yield式でyの2乗をやっていると、1と2と4の2乗としてSeq(1, 4, 16)が計算でき、モナドになってなくてもmapかflatMapを持っていれば、何でもfor式として使えるような構造になっています。なぜそんなことになっているかというと、あくまでfor-yieldは糖衣構文、シンタックスシュガーとして作られています。
for-yieldで書かれた式が、コンパイル時にはmapとflatMapを使った式に変換される感じです。(視聴者の「最初do式の互換だと思ってたら混乱しました」というコメントを受けて)僕も最初はdo式の互換だと思っていましたが、実はdo式よりはモナド内包表記なんです。HaskellのreturnとScalaのyieldが対応しているのかと思っていたら、実はそんなことなくて混乱していたんですが。
Scalaのfor式はHaskellのdo構文だよ、という説明をたまに見かけますが、逆に混乱を生む場合もあります。導入されたモチベーションはHaskellのdo式と似ていますが、for構文としてはdo構文よりも内包表記のほうが意味合いとしては近しいものだと覚えてもらえると、たぶんいいかなという気がしています。
シンタックスシュガー(糖衣構文)でmapかflatMapに変換される話ですが、上のように書いたfor-yieldの式は下のように変換されます。最初のSeq("1", "2", "abc", "4")の部分がflatMapのメソッド呼び出しになって、x =>が付いてさらにtryのtoOptionが付いて、そこにmapの呼び出しになる。
一番最後のところでy * yになるような感じで変換されます。今、二重のネストなのでflatMapとmapになっていますが、基本的に二重、三重、四重になってくると、flatMap、flatMap、flatMapとなって、最後のyield部分がmapに近しい値になる、mapに変換されるような構造になります。
for-yieldの結果がどんな型になるかは、最初のジェネレータ、最初の<ーの右側の型を見ると判明します。ここもSeqのflatMapになるので、全体としてはSeqになるだろうな、という感じです。
最初がVectorであれば、VectorのflatMapになるので、for式全体もVectorになりますよ。逆に、最初のx <- の右側がリストなので、リストのflatMapになり、for式全体がリストになります。こういった感じでfor式全体の型を解釈できます。
たいていはそこを見れば解決しますが、最初の部分で決定できない場合がたまにあります。例えばMapです。Mapの場合、最後のyield式がタプルになっていれば、Mapに変換できますが、タプルになっていないとMapをflatMapにしても、Mapにできないので、Iterableになるケースがあるんですね。
そのため、Mapが使われていると、yield部分を見ればfor式全体の型が何なのかがわかるかな、という感じです。
まとめにいくと、for-yieldはScalaにおける内包表記です。ゆるふわなので、mapとflatMapをもっていれば全部コンパイラは通します。最初の部分の型がわかれば、だいたい結果の型がわかります。
(視聴者の「ポリモフィズムでIterable解決している?」というコメントを受けて)そうですね。MapのflatMapメソッドが戻り値の型をMapにするのかIterableにするのかは、けっこう難しいメカニズムを使っていて。渡されるファンクションの型によって結果の型が変わるので、それはけっこうScalaの型システムのおもしろポイントですね。また2.12と2.13でこの辺の実装がちょっと変わっていたりするので、興味のある方は深追いしてみるといいかもしれません。
(次回につづく)
2025.03.17
いくら読書をしても「成長しない人」が見落としていること 10分でできる「正しい学び方」
2025.03.17
ソフトバンクとOpenAIにとって「歴史的な日」になった 孫正義氏が語る、AI革命の全ぼう
2025.03.17
不確実な時代だからこそ「知らないこと」を武器にする ハーバード主席卒業生の逆説的なメッセージ
2025.03.19
部下の「タスクの先延ばし」が少ない上司の特徴とは? 研究が示す、先延ばし行動を減らすリーダーの条件
2025.03.18
フェデラー氏が語る「努力しない成功は神話」という真実 ダートマス卒業生に贈る勝利の秘訣
2025.03.18
全知全能の最先端AI「Cristal」が企業の大脳となる ソフトバンク孫正義氏が語る、現代における「超知性」の可能性
2025.03.12
SNSで炎上している研究者は「研究者として正しい」 人文学のプロ・阿部幸大氏が説く“強い意見を出せない時代”に対する考え方
2025.03.14
三流の上司と一流の上司の違い 部下の心を動かす科学的アプローチ
2025.01.07
1月から始めたい「日記」を書く習慣 ビジネスパーソンにおすすめな3つの理由
2025.03.13
改正後のiDeCoと退職金の受け取り方の事例 「改悪」は本当か? プロが真相と狙いを解説
【手放すTALK LIVE#046】 出版記念イベント 『大きなシステムと小さなファンタジー』 一つ一つのいのちが大切にされる社会へ
2025.02.03 - 2025.02.03
「聴く」から始まる組織変革 〜篠田真貴子さんと考える対話型マネジメント〜
2025.02.14 - 2025.02.14
「目の前の利益を優先する」心理とは:ビジネスに活かせる意思決定の科学
2025.02.12 - 2025.02.12
新刊『組織をダメにするのは誰か?職場の問題解決入門』出版記念セミナー
2025.02.04 - 2025.02.04
会社の体質、これまでどおりで大丈夫? 職場に新たな風を吹き込むための「ネガティブ・ケイパビリティ」入門
2025.02.10 - 2025.02.10