Scala Language
변경 불가능한 스타일로 데이터 작업하기
수색…
비고
값과 변수 이름은 더 낮은 낙타의 경우 여야합니다.
상수 이름은 위 낙타 경우이어야합니다. 즉, 멤버가 final이며 불변이며 패키지 객체 또는 객체에 속한 경우, 상수로 간주 될 수 있습니다
방법, 값 및 변수 이름은 더 낮은 낙타의 경우 여야합니다.
출처 : http://docs.scala-lang.org/style/naming-conventions.html
이 컴파일 :
val (a,b) = (1,2)
// a: Int = 1
// b: Int = 2
하지만 그렇지 않습니다.
val (A,B) = (1,2)
// error: not found: value A
// error: not found: value B
val과 var가 아닌
val
과 var
scala> val a = 123
a: Int = 123
scala> a = 456
<console>:8: error: reassignment to val
a = 456
scala> var b = 123
b: Int = 123
scala> b = 321
b: Int = 321
-
val
참조는 변경할 수 없습니다.Java
의final
변수와 같이 일단 초기화되면 변경할 수 없습니다. -
var
참조는 Java에서 간단한 변수 선언으로 재 할당 가능
변경할 수없는 컬렉션과 변경 가능 컬렉션
val mut = scala.collection.mutable.Map.empty[String, Int]
mut += ("123" -> 123)
mut += ("456" -> 456)
mut += ("789" -> 789)
val imm = scala.collection.immutable.Map.empty[String, Int]
imm + ("123" -> 123)
imm + ("456" -> 456)
imm + ("789" -> 789)
scala> mut
Map(123 -> 123, 456 -> 456, 789 -> 789)
scala> imm
Map()
scala> imm + ("123" -> 123) + ("456" -> 456) + ("789" -> 789)
Map(123 -> 123, 456 -> 456, 789 -> 789)
스칼라 표준 라이브러리는 불변의 데이터 구조와 변경 가능한 데이터 구조를 제공한다. 변경할 수없는 데이터 구조가 "수정"될 때마다 원본 컬렉션을 현재 위치에서 수정하는 대신 새 인스턴스가 생성됩니다. 컬렉션의 각 인스턴스는 다른 인스턴스와 중요한 구조를 공유 할 수 있습니다.
변경 가능하고 변경 불가능한 콜렉션 (공식 스칼라 문서)
하지만이 경우에는 불변성을 사용할 수 없습니다!
2 Map
을 취하고 ma
와 mb
모든 요소를 포함하는 Map
을 반환하는 함수를 예제로 선택해 보겠습니다.
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int]
첫 번째 시도는 for ((k, v) <- map)
사용하여지도 중 하나의 요소를 반복하고 병합 된지도를 반환 할 수 있습니다.
def merge2Maps(ma: ..., mb: ...): Map[String, Int] = {
for ((k, v) <- mb) {
???
}
}
이 첫 번째 움직임은 즉시 구속을 추가 돌연변이를 그 외부 for
지금 필요합니다. 해제 절임 때 더 명확하다 for
:
// this:
for ((k, v) <- map) { ??? }
// is equivalent to:
map.foreach { case (k, v) => ??? }
"왜 우리가 돌연변이를해야합니까?"
foreach
는 부작용에 의존합니다. 우리가 foreach
내에서 어떤 일이 일어나길 원할 때마다 우리는 "부작용"이 필요합니다.이 경우 변수 var result
변경하거나 변경 가능한 데이터 구조를 사용할 수 있습니다.
result
맵 작성 및 채우기
의는 가정하자 ma
와 mb
있습니다 scala.collection.immutable.Map
, 우리가 만들 수 있습니다 result
에서지도 ma
:
val result = mutable.Map() ++ ma
그런 다음 요소를 추가하는 mb
반복하고 ma
에있는 현재 요소의 key
가 이미있는 경우 mb
대체합니다.
mb.foreach { case (k, v) => result += (k -> v) }
변경 가능한 구현
지금까지는 "변경 가능한 모음을 사용해야했습니다."라고 말하면 올바른 구현이 될 수 있습니다.
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int] = {
val result = scala.collection.mutable.Map() ++ ma
mb.foreach { case (k, v) => result += (k -> v) }
result.toMap // to get back an immutable Map
}
예상대로 :
scala> merge2Maps(Map("a" -> 11, "b" -> 12), Map("b" -> 22, "c" -> 23))
Map(a -> 11, b -> 22, c -> 23)
구조에 접히기
이 시나리오에서 foreach
를 제거하려면 어떻게해야합니까? 우리가해야할 일은 기본적으로 콜렉션 요소를 반복하고 옵션을 누적하는 동안 함수를 적용하는 것입니다. 옵션을 .foldLeft
:
def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int] = {
mb.foldLeft(ma) { case (result, (k, v)) => result + (k -> v) }
// or more concisely mb.foldLeft(ma) { _ + _ }
}
이 경우 우리의 "결과"는 .foldLeft
의 zero
인 ma
부터 시작하는 누적 값입니다.
중간 결과
분명히 이것은 불변의 솔루션은 생산 및 많은 파괴 Map
접는 동안 인스턴스를하지만, 그 인스턴스의 전체 클론 아니라는 것을 언급 할 가치가 Map
축적 대신 기존 인스턴스에 중요한 구조 (데이터를) 공유하고 있습니다.
더 쉬운 합리성
.foldLeft
접근법처럼 선언적이라면 의미에 대해 추론하는 것이 더 쉽습니다. 불변의 데이터 구조를 사용하면 우리의 구현을 쉽게 추론 할 수 있습니다.