Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 경제
- 러닝 스칼라
- BOF
- webhacking
- Linux
- Scala
- 딥러닝
- hackerschool
- 러닝스칼라
- Shellcode
- c
- BOF 원정대
- c++
- 백엔드
- deep learning
- Javascript
- 인공지능
- ChatGPT
- php
- mysql
- flask
- 파이썬
- 챗GPT
- Python
- backend
- Web
- hackthissite
- 리눅스
- 웹해킹
- hacking
Archives
- Today
- Total
jam 블로그
러닝스칼라 7장 - 그 외의 컬렉션 본문
728x90
불변의 컬렉션
- List, Set, Map 은 불변의 컬렉션
- 코드의 안정성을 높이고 버그를 방지하기 위해 데이터와 데이터 구조는 변경되거나 자신의 상태를 바꿀 수 없다.
가변 컬렉션 생성하기
- collection.mutable은 자동으로 추가되지 않아서 반드시 포함해야한다.
//불변
collection.immutable.Buffer
collection.immutable.Set
collection.immutable.Map
//가변
collection.mutable.Buffer
collection.mutable.Set
collection.mutable.Map
- 가변 컬렉션을 이용하면 추가나 변경을 할 수 있다.
- toList , toSet, toMap 메소드를 이용해서 가변 컬렉션을 불변의 컬랙션으로 만들 수 있다.
- toBuffer 메소드를 이용해서 불변의 컬렉션을 가변적인 컬렉션으로 변경할 수 있다.
- 버퍼의 단점이 너무 광범위하다는 것인데 이것을 빌더(builder)로 해
컬렉션 빌더 사용하기
- Builder는 Buffer를 단순화한 형태로, 할당된 컬렉션 타입을 생성하고 추가연산만 지원
scala> val b = Set.newBuilder[Char]
b = scala.collection.mutable.SetBuilder@3789ad3d
scala> b += 'h'
scala.collection.mutable.SetBuilder@3789ad3d
scala> b ++= List('e', 'l', 'l', 'o')
scala.collection.mutable.SetBuilder@3789ad3d
scala> val helloSet = b.result
helloSet: scala.collection.immutable.Set[Char] = Set(h, e, l, o)
- 불변의 컬렉션을 전환하기 위해 단지 가변적인 컬렉션을 반복적으로 생성한다면 Builder
- 빌더는 자신의 타입에 대응하는 불변의 타입이 무엇인지 알고 있음
- 빌더의 장점?
배열
- Array는 고정된 크기를 가지며, 내용을 변경할 수 있으며, 인덱스를 가지고 있다.
- 자바 배열은 toString() 메소드를 재정의 하지 않기 때문에 타입 매개변수와 참조를 출력, 스칼라는 재정의
scala> val colors = Array("red" ,"green", "blue")
scala> colors(0) = "purple"
Array[String] = Array(purple, green, blue)
scala> println("vert purple : " + colors)
vert purple : [Ljava.lang.String;@6f76b33e
val files = new java.io.File(".").listFiles
Array(./.jupyter, ./Untitled.ipynb, ./.ipynb_checkpoints, ./Untitled1.ipynb, ./Untitled2.ipynb)
val scala = files map (_.getName) filter(_ endsWith "ipynb")
Array(Untitled.ipynb, Untitled1.ipynb, Untitled2.ipynb)
- JVM 코드를 위해 필요한 경우가 아니라면 이 타입을 사용하는 것은 권하지 않는다 왜?
Seq와 시퀀스
- Seq는 오든 시퀀스의 루트 타입으로 List , Vector도 포함
- Seq는 인스턴스화 할 수 없지만 List를 생성하는 방법으로 Seq를 호출 할 수 있다.
scala> val inks = Seq('C','M','Y','K')
inks = List(C, M, Y, K)
- Vector : 색인된 접근을 위해 Array에 의해 지원받는 리스트
- List : n번쨰 항목에 접근하려면 헤드부터 탐색
- String : Char 구성 요소를 가지는 시쿼스
스트림
- Stream 타입은 하나 또는 그 이상의 시작 요소들과 재귀함수로 생성되는 지연(lazy) 컬랙션
- 스트림의 구성 요소들은 나중에 추출될 떄를 대비하여 캐시에 저장되어 각 요소가 한번만 생성됨을 보장
- Stream.Empty로 종료될 수 있고 무한히 커질수 있다.
- List 와 마찬가지로 헤드와 테일로 구성된 재귀적인 데이터 구조
scala> def inc(i: Int) : Stream[Int] = Stream.cons(i,inc(i+1))
scala> def inc(head: Int): Stream[Int] = head #:: inc(head+1)
inc: (i : Int)Stream[Int]
scala> val s = inc(1)
s: Stream[Int] = Stream(1,?)
scala> val l = s.take(5).toList
List(1,2,3,4,5)
scala> s
Stream(1,2,3,4,5,?)
scala> def to(head:Char, end:Char):Stream[Char] = (head > end) match {
case true => Stream.empty
case false => head #:: to((head+1).toChar , end)
}
to: (head: Char, end: Char)Stream[Char]
scala> val hexChars = to('A','F').take(20).toList
hexChars = List(A, B, C, D, E, F)
모나딕
- Iterable 연산 같은 변형 연산을 지원하지만 하나 이상의 요소는 포함할 수 없다.
- 잠재적 값을 나타내며 추가 연산을 연결하거나 값을 추출하는 안전한 연산 제
Option 컬렉션
- 크기가 1이 넘지 않는 컬렉션으로 단일값의존재 또는 부재를 나타냄
- 널(null) 값의 안전한 대체제, NullPointException을 일으킬 가능성이 즐어듬
- 타입-매개변수화된 컬랙션인 Some 과 빈 컬렉션인 None
- 함수 결과를 처리하는데 type-safe 방식을 제공
scala> def divid(amt: Double, divisor:Double): Option[Double] = {
if(divisor == 0) None
else Option(amt/divisor)
}
divid: (amt: Double, divisor: Double)Option[Double]
scala> val legit = divid(5,2)
legit = Some(2.5)
scala> val illegit = divid(3,0)
illegit = None
- headOption 과 find 연산
scala> val odds = List(1,3,5)
scala> val events = odds filter (_ % 2 ==0)
events = List()
scala> val firstEven = events.headOption
firstEven = None
scala> val words = List("risible", "scavenger", "gist")
scala> val uppercase = words find (w => w == w.toUpperCase)
Option[String] = None
scala> val lowercase = words find ( w => w == w.toLowerCase)
Option[String] = Some(risible)
Option으로부터 값 추출하기
- get() 메소드도 있지만 안전하지 않다, None에 get 을 한다면 no such element 에러가 발생하기 때문이다.
- 누란된 갑슬 처리하는 대체값이 있는 fold나 getorElse를 사용하자
Name | Example | 설명 |
---|---|---|
fold | nextOption.fold(-1)(x => x) | Some인 경우 주어진 함수로부터 추출한 값을 None인 경우 시작값을 반환함, 그외 다른 fold나 reduce도 마찬가지 |
getOrElse | nextOption getOrElse 5 | Some의 값을 반환하거나 아니면 이름 매개변수의 결과를 반환 |
orElse | nextOption orElse nextOption | 실제로 값을 추출하지는 않지만 None 인 경우 값을 채우려함 |
매치 표현식 | nextOption match { case Some(x) => x ; case None => -1 | 값이 있는 경우 매치표현식으로 그 값을 처리함 |
- null 을 쓰는 것보다 더 안전한 방법, 그 이유는 모든 널 포인트 에러를 방지 못하기 때문
Try 컬렉션
- util.Try 컬렉션은 에러 처리를 컬랙션 관리로 바꿔 놓는다.
- 스칼라에서는 예외(Exception)을 발생시켜 에러를 일으킬 수 있다.
- 예외를 발생시키기 위해서는 Exception 키워드와 함께 throw를 사용
scala> def loopAndFail (end:Int , failAt: Int ) : Int = {
for ( i <- 1 to end ) {
println(s"$i) " )
if (i == failAt ) throw new Exception("Too many iterations")
}
end
}
- catch 보단 util.Try 만 사용하는 것을 추천, 그 이유는 에러를 처리하기에 더 안전하고 완전한 모나딕 접근법
- util.Try 는 Success 와 Failure 을 가지고 있다.
scala> val t1 = util.Try( loadAndFail(2,3))
1)
2)
Success(2)
scala> val t2 = util.Try ( loadAndFail(4,2))
1)
2)
Failure(java.lang.Exception: Too many iterations)
- Try 에러 처리 메소드
Name | Example | 설명 |
---|---|---|
flatMap | nextError flatMap { _ => nextError } | Success인 경우 util.Try를 반환하는 함수를 호출함으로써 현재의 반환값을 새로운 내장된 반환값(또는 예외에 매핀함, 우리의 ‘nextError’ 데모함수는 입력값을 취하지 않기 때문에 우리는 현재 Success로 부터 사용하지 않는 입력값을 나타내는 언더스코어를 사용 |
foreach | nextError foreach(x => println(“success!” + x)) | Success인 경우 주어진 함수를 한 번 실행하고, Failure 일 때는 실행하지 않 |
getOrElse | nextError getOrElse 0 | Success에 내장된 값을 반환하거나,Failure인 경우 이름에 의한 매개변수의 결과를 반환함 |
orElse | nextError orElse nextError | flatMap의 반대되는 메소드, Failure인 경우 그 역시Try를 반환하는 함수를 호출함.orElse로 어쩌면 Failure를 Success로 전환 할 수 있음 |
toOption | nextError.toOption | Try를 Option으로 전환하여 Success는 Some으로 Failure는 None이 됨. 내장된 Exception을 잃게 된다는 단점이 있음 |
map | nextError map (_ * 2) | Success인 경우 새로운 값에 내장된 값을 매핑하는 함수를 호출함 |
매치표현식 | nextError match { case util.Success(x) => x; case util.Failure(error) => -1 } | Success를 (’x’에 저장된) 반환값으로 또는 Failure를 (’error’에 저장된) 예외로 처리하기 위해 매치 표현식을 사용함. |
아무일도 하지않음 | nextError | 가장 쉬운 에러 처리 방식으로, 내가 개인적으로 선호하는 방식임. 이 방식으로 단순히 예외가 잡히거나,현재의 애플리케이션을 종료시킬 때까지 호출 스택을 타고 전파되도록 그대로 둠. 이 방식은 민감한 경우에 사용하면 문제가 크겠짐나, 발새한 예왼느 결코 무시되지 않음 |
scala> val input = " 123 "
input = " 123 "
scala> val result = util.Try(input.toInt) orElse util.Try(input.trim.toInt)
result = Success(123)
scala> result foreach { r => println(s"Parsed '$input' to $r!" )}
Parsed ' 123 ' to 123!
scala> val x = result match {
case util.Success(x) => Some(x)
case util.Failure(ex) => {
println(s"$input")
None
}
}
x = Some(123)
퓨처 컬렉션
- 백그라운드 작업을 개시하는 concurrent.Future
- Option과 Try 와는 달리 즉시 사용하지 못할수 있다.
- 퓨처를 함수로 호출하면 현행 스레드와는 별개의 스레드에서 그 함수를 실행시킨다.
scala> import concurrent.ExecutionConscala 3.Implicits.global
scala> val f = concurrent.Future{println("hi")}
Message: <console>:25: error: Cannot find an implicit ExecutionConscala 3.
scala> val f = concurrent.Future{Thread.sleep(5000);println("hi")}
scala> println("waiting")
waiting
hi
- 퓨처의 작업이 완료될때 실행할 콜백 함수를 설정 가능
- 백그라운드 작업이 끝날때까지 main 스레드를 차단하고 기다리는 기능
비동기식 퓨처 연산
Name | Example | 설명 |
---|---|---|
failbackTo | nextFtr(1) failbackTo nextFtr(2) | 두번째 퓨쳐를 첫번째 연결하고 새로운 종합적인 퓨처를 반환함. 첫 번째 퓨처가 성공적이지 않다면 두 번째 퓨처가 호출 |
flatMap | nextFtr(1).flatMap(int => nextFtr()) | 두번째 퓨처를 첫 번째에 연결하고 새로운 종합적인 퓨처를 반환함. 첫 번째가 성공적이라면 그 반환 값이 두 번째를 호출하는데 사용됨 |
map | nextFtr(1) map (_ * 2) | 주어진 함수를 퓨처에 연결하고 새로운 종합적인 퓨처를 반환함. 퓨처가 성공적이라면 그 반환 값이 해당 함수를 호출할 때 사용됨 |
onComplete | nextFtr() onComplete { _ getOrElse 0 } | 퓨처의 작업이 완료된 후 주어진 함수가 값 또는 예외를 포함한 Try를 이용하여 호출됨 |
onFailure | nextFtr() onFailure { case _ => “Error!” } | 퓨처의 작업이 예외를 발생시키면 주어진 함수는 그 예외를 가지고 호출됨 |
onSuccess | nextFtr() onSuccess { case _ => s“Got $x” } | 퓨처의 작업이 성공적으로 완료되었으면 주어진 함수는 그 반환값을 가지고 호출 |
Future.sequence | concurrent.Future sequence List(nextFtre(1),nextFtr(5)) | 주어진 시퀀스에서 퓨처를 벙행으로 실행하여 새로운 퓨처를 반환함. 시퀀스 내의 모든 퓨처가 성공하면 이들의 반환값의 리스트가 반환됨. 그렇지 않으면 그 시퀀스내에서 처음으로 발생한 예외가 반환됨 |
import concurrent.ExecutionConscala 3.Implicits.global
import concurrent.Future
import scala.io.Source
def cityTemp(name:String): Double = {
val url = "http://api.openweathermap.org/data/2.5/weather"
val cityUrl = s"$url?&APPID=72bf27b20bf867de6236aca3a257e0d1&q=$name"
val json = io.Source.fromURL(cityUrl).mkString.trim
val pattern = """.*"temp":([\d.]+).*""".r
val pattern(temp) = json
temp.toDouble
}
val cityTemps = Future sequence Seq(
Future(cityTemp("Seoul")), Future(cityTemp("Jeju"))
)
cityTemps onSuccess {
case Seq(x,y) if (x>y) => println(s"Seoul is warmer: $x K")
case Seq(x,y) if (y>x) => println(s"Jeju is warmer: $x K")
}
동기식 퓨처 처리
- 백그라운드 스레드가 완료되기를 기다리는 동안 메인 스레드를 차단
- concurrent.Await.result() 사용
- 주어진 시간보다 빠르게 처리되면 결과가 반환되지만 아니라면 java.util.concurrent.TimeoutException 발생
scala> def nextFtr( i: Int = 0 ) = Future {
def rand(x:Int) = util.Random.nextInt(x)
Thread.sleep(rand(5000))
if(rand(3)>0) (i +1) else throw new Exception
}
nextFtr: (i: Int)scala.concurrent.Future[Int]
scala> import concurrent.duration._
scala> val amount = concurrent.Await.result(nextFtr(5), maxtime)
amount: Int = 6
scala> val amount = concurrent.Await.result(nextFtr(5), maxtime)
java.lang.Exception
'IT Book Study > 러닝 스칼라' 카테고리의 다른 글
러닝스칼라 9장 - 객체, 케이스 클래스, 트레이트 (0) | 2020.09.28 |
---|---|
러닝스칼라 8장 - 클래스 (0) | 2020.09.28 |
러닝스칼라 6장 - 보편적인 컬렉션 (0) | 2020.09.28 |
러닝스칼라 5장 - 일급 함수 (0) | 2020.09.28 |
러닝스칼라 4장 - 함수 (0) | 2020.09.28 |
Comments