この記事はウェブクルー Advent Calendar 2023 23日目の記事です。
はじめに
最近scalaの案件に入る機会があったので、メモ程度ですが書いていきます。
よく分からないまま使っていた謎の矢印について調べたので記載します。
<- (Generator)
ジェネレータとは、反復処理やコレクションから要素を順に取り出すための仕組みを指すそうです。
主にfor式の中で使用し、以下のような形で使われます。
コレクション反復処理
<-を使ってリストを順番に取り出し、変数にバインドします。
val numbers = List(1, 2, 3, 4, 5)
// for式を使用したリストの反復処理
for (n <- numbers) {
println(n)
}
パターンマッチング
<-を使ってタプルの要素を取り出し、変数にそれぞれバインドします。
val pairs = List((1, "one"), (2, "two"), (3, "three"))
// for式を使用したタプルのパターンマッチング
for ((number, word) <- pairs) {
println(s"Number: $number, Word: $word")
}
フィルタリング
<-を使用して要素を取り出し、ifで条件を指定することで特定の条件を満たす要素を抽出することができます。
val numbers = List(1, 2, 3, 4, 5)
// for式を使用したフィルタリング
val evenNumbers = for {
n <- numbers
if n % 2 == 0
} yield n
println(evenNumbers) // List(2, 4)
ジェネレータ
例えば、以下のように2つのジェネレータを使用して、ペアを生成することができます。
<-は要素を取り出し、組み合わせたタプルを作成します。
val pairs = for {
x <- 1 to 3
y <- 'a' to 'c'
} yield (x, y)
println(pairs)
// Vector((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c))
=> (Function Literal)
関数リテラルとは、変数に関数を代入して記述することを指すそうです。
主に関数の定義やパターンマッチングなどに使用され、以下のような形で使います。
関数の定義
val addOne: Int => Int = (x: Int) => x + 1
左辺のInt=>Int
では関数の型を示しており、引数をIntでもらってきて、=>Int
で返り値の値をIntで指定しています。
また、右辺では(x: Int)
で引数を取ってx + 1
の値を返すラムダ式を示しています。
ラムダ式は無名関数を定義するために使われ、構文を簡潔に記載できたり、mapやfilterなどを組み合わせて読みやすく記載できます。
パターンマッチング
ケースの部分と処理内容の紐付けに使用します。
val animal: String => String = {
case "cat" => "excellent!"
case "dog" => "nice!"
case _ => "so cute!"
}
インポート
以下のように記載すると、DateをSQLDateで使えるようになります。
import java.util.Date
import java.sql.{Date => SQLDate}
また、とあるパッケージ内の全てのtypeをimportする場合import package._
のように記載すると思いますが、特定のtype飲み除外したい場合はこんな風にも記載できるそう。
(例)java.util.Dateを除くjava.util._をimportする場合
import java.util.{Date => _, _}
名前渡しパラメータ
以下のようにすると、名前渡しのパラメータとして使えます。
def calculate(input: => Int) = input * 37
※名前渡しパラメータとは
通常ではdef calculate(input:Int) = input * 37
のように記載すると思いますが
名前渡しにすることで、その引数が必要になる瞬間まで遅延されます。
例えば以下の場合、関数内でresultが使用されるまでnum + 5
は遅延されます。
なので"Before calculation"
の表示後に評価が行われ"Calculated result: 47
が表示されます。
計算が副作用を伴う場合などによいらしいです。
(例)
def calculateAndLog(num: Int): Int = {
try {
// ファイルにログを書き込む(副作用)
val logFile = new PrintWriter(new FileWriter("log.txt", true))
logFile.println(s"Calculating with input: $num")
logFile.close()
// 通常の計算
num * 2
} catch {
case e: IOException =>
println(s"Error writing to log file: ${e.getMessage}")
0
}
}
// 関数呼び出し
val result = calculateAndLog(10)
おまけ
タプルへのアクセスでスッキリ書く方法を知ったのでメモ。
例えば、以下のような要素を持つタプルがあったとして...
val xmas: (Int, String, String) = (1, "七面鳥", "ターキーレッグ")
以下のようにパターンマッチで分解して書けるそう↓
val (number,material,recipe) = xmas
logger.info(s"番号: ${number}")
logger.info(s"材料: ${material}")
logger.info(s"料理: ${recipe}")
おわりに
思っていたより色んな使い方があることが分かりました!
しっかり理解して使っていこうと思います。
明日、24日は@wc-maruyamaさんの投稿になります。
よろしくお願いします!
ウェブクルーでは一緒に働いてくれる方を絶賛募集中です!
興味のある方はぜひお問い合わせください。
https://www.webcrew.co.jp/recruit/
参照