for내장에는 기본형태
→ 제너레이터 식이 최소 한개 들어가고, 조건을 설정해주는 가드가 들어갈 수도 있다.
package progscala2.forcomps
object RemoveBlanks {
/**
* 지정한 입력 파일에서 빈 줄을 제거한다.
*/
def apply(path: String, compressWhiteSpace: Boolean = false): Seq[String] =
for {
line <- scala.io.Source.fromFile(path).getLines.toSeq // <1>
if line.matches("""^\\s*$""") == false // <2>
line2 = if (compressWhiteSpace) line replaceAll ("\\\\s+", " ") // <3>
else line
} yield line2 // <4>
/**
* 지정한 입력 파일에서 빈 줄을 제거하고 남은 줄들을 표준 출력에
* 하나씩 보낸다.
* @param args 파일 경로 목록이다. 파일 이름의 앞에 '-'를 붙이면
* 파일에서 연속된 여러 공백을 하나로 압축해준다.
*/
def main(args: Array[String]) = for {
path2 <- args // <5>
(compress, path) = if (path2 startsWith "-") (true, path2.substring(1))
else (false, path2) // <6>
line <- apply(path, compress)
} println(line) // <7>
}
for 내장 문법은 실제로는 컬렉션 메서드인 foreach, map, flatMap, withFilter를 호출하는 것에 대한 구문상의 편의일 뿐이다.
이런 메서드를 호출하는 다른방법을 제공하는 이유는, 무엇보다 훨씬 읽기 쉽고 쓰기 쉽기 때문이다.
우선 간단한 for내장의 예시
val states = List("Alabama", "Alaska", "Virginia", "Wyoming")
for {
s <- states
} println(s)
// 결과:
// Alabama
// Alaska
// Virginia
// Wyoming
states foreach println
// 결과는 이전과 같음
→ 결국 for 내장뒤에 yield가 없는 단일 제너레이터는 foreach를 호출한 것과 같다.
→ 즉 주어진 변수를 하나하나 출력하는 거지 별도의 컬렉션을 호출하지는 않음.
yield를 사용한 경우
val states = List("Alabama", "Alaska", "Virginia", "Wyoming")
for {
s <- states
} yield s.toUpperCase
// 결과: List(ALABAMA, ALASKA, VIRGINIA, WYOMING)
states map (_.toUpperCase)
// 결과: List(ALABAMA, ALASKA, VIRGINIA, WYOMING)
→ 제너레이터가 하나만 있고 yield가 있는 식은 map을 호출하는 것과 같다.
generator가 있는 경우
val states = List("Alabama", "Alaska", "Virginia", "Wyoming")
for {
s <- states
c <- s
} yield s"$c-${c.toUpper}"
// 결과: List("A-A", "l-L", "a-A", "b-B", ...)
states flatMap (_.toSeq map (c => s"$c-${c.toUpper}"))
// 결과: List("A-A", "l-L", "a-A", "b-B", ...)