2023 01 25
2023-01-25¶
Scala 기초 투어¶
TIPS¶
- 스칼라에서
_는 자바의*을 연상하면 될 듯
고차 함수¶
-
개요
- 함수를 파라미터로 받거나, 함수를 결과값으로 리턴하거나
- 함수 자체를 First Class Value (일급 객체)
- 함수를 ㄹㅇ 일반 값처럼 다룰 수 있어야 함
- 함수가 일급 객체라 고차함수로 쓰기 좋음
- 일상적인 예
map
- 메서드를 함수로 강제 변환
convertCtoF를x => convertCtoF(x)로 변환
- 고차함수 쓰는 이유
- 중복되는 코드를 고차함수를 통해 줄일 수 있다
object SalaryRaiser { private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] = salaries.map(promotionFunction) def smallPromotion(salaries: List[Double]): List[Double] = promotion(salaries, salary => salary * 1.1) def greatPromotion(salaries: List[Double]): List[Double] = promotion(salaries, salary => salary * math.log(salary)) def hugePromotion(salaries: List[Double]): List[Double] = promotion(salaries, salary => salary * salary) }
- 중복되는 코드를 고차함수를 통해 줄일 수 있다
- 함수를 리턴하는 함수
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = { val schema = if (ssl) "https://" else "http://" (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query" } val domainName = "www.example.com" def getURL = urlBuilder(ssl = true, domainName) val endpoint = "users" val query = "id=1" val url = getURL(endpoint, query) // "https://www.example.com/users?id=1"
중첩 함수¶
- 개요
- 메서드를 중첩시켜 메서드를 정의할 수 있다
Multiple Parameter List¶
- 개요
- 메서드는 파라미터 파라미터 리스트를 가질 수 있음
-
Iterable trait를 봐보자
foldLeft는 두 파라미터 함수 op를 초기값 z에 적용
- 예시
- 타입추론 할 수 있도록 써주세요
Case Class¶
- 개요
- 불변 데이터 모델링에 유용
- 케이스 클래스 정의
// 정의하는 방법 case class Book(isbn: String) val frankenstein = Book("987-93939212") // 예시 case class Message(sender: String, recipient: String, body: String) val message1 = Message("joel610@naver.com", "luckyys610@unist.ac.kr", "hello!") println(message1.sender) // joel610@naver.com message1.sender = "joel610naver@gmail.com" // 안대! 내부적으로 val 쓰거든
- 비교
- 케이스 클래스 인스턴스는 레퍼런스로 비교 안해
- 구조 그 자체로 비교됨
- 복제
copy메서드를 통해서는 안의 인스턴스 변수 얕은 복사가 됨
패턴 매칭¶
- 개요
- 자바의 switch와 같아
- if-else 대신 써볼래요?
- 문법
- 활용
sealed trait Notification case class Email(sender: String, title: String, body: String) extends Notification case class SMS(caller: String, message: String) extends Notification case class VoiceRecording(contactName: String, link: String) extends Notification def showNotification(notification: Notification): String = { notification match { case Email(sender, title, _) => s"You got an email from $sender with title: $title" case SMS(number, message) => s"You got an SMS from $number! Message: $message" case VoiceRecording(name, link) => s"You received a Voice Recording from $name! Click the link to hear it: $link" } } val someSms = SMS("12345", "Are you there?") val someVoiceRecording = VoiceRecording("Tom", "recording.org/id/123") println(showNotification(someSms)) println(showNotification(someVoiceRecording))
- 패턴 가드
- boolean expresion을 통해 조건을 걸어 패턴 가드를 걸 수 있다
def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { notification match { case Email(sender, _, _) if importantPeopleInfo.contains(sender) => "You got an email from special someone!" case SMS(number, _) if importantPeopleInfo.contains(number) => "You got an SMS from special someone!" case other => showNotification(other) } } val importantPeopleInfo = Seq("123-456", "joel610@naver.com") val importantSms = SMS("123-456", "hi!") val importantEmail = Email("joel610@naver.com", "Hello!", "hi!") println(showImportantNotification(importantSms, importantPeopleInfo)) println(showImportantNotification(importantEmail, importantPeopleInfo))
- boolean expresion을 통해 조건을 걸어 패턴 가드를 걸 수 있다
- 타입만 가지고도 휘뚜루 마뚜루
- 타입으로만 매칭 시켜서 액션시킬 수 있음
sealed trait Device case class Phone(model: String) extends Device { def screenOff = "Turning screen Off" } case class Computer(model: String) extends Device { def screenSaverOn = "Turning screen saver on" } def goIdle(device: Device): String = device match { case p: Phone => p.screenOff case c: Computer => c.screenSaverOn }
- 타입으로만 매칭 시켜서 액션시킬 수 있음
- Sealed Types
sealed키워드가 있다면, 해당 trait/abstract class의 자식은 같은 파일내에서만 정의해야해!- 이러면 match 같은거 쓸 때 다른 곳에 자식없다는게 명확해지니 개꿀이겠지?
싱글턴 오브젝트¶
- 개요
- 최초 참조시에 lazy init
- 딱 하나만 만들어짐
- Java의 static이랑 느낌이 비슷
- 문법
object키워드
-
Companion objects
- 클래스와 같은 이름의 오브젝트(싱글턴)을 companion object 라고 부름
- 오브젝트와 같은 이름의 클래스를 companion class 라고 부름
- companion object에 factory method를 넣어둘 수도 있어
class Email(val username: String, val domainName: String) object Email { def fromString(emailString: String): Option[Email] = { emailString.split('@') match { case Array(a, b) => Some(new Email(a, b)) case _ => None } } } val scalaCenterEmail = Email.fromString("scala.center@epfl.ch") scalaCenterEmail match { case Some(email) => println(email.username, email.domainName) case None => println("Fail!") }
REGEX¶
- 개요
.r하나면 모든 String이 Regex로 변경된다
- 활용
함수형 프로그래밍에서의 커링¶
- 참고: https://captainthomas.tistory.com/entry/%EC%8A%A4%EC%B9%BC%EB%9D%BC-%EC%BB%A4%EB%A7%81Currying-%EC%95%84%EB%A7%88%EB%8F%84-%EC%B2%AB%EB%B2%88%EC%A7%B8-%EA%B8%80
-
디폴트 파라미터에 적용가능
def Adder(x: Int, y: Int): Int = x + y // 커링을 한다면 def AdderDefaultX(x: Int)(y: Int): Int = x + y def AdderDefaultY(y: Int)(x: Int): Int = x + y // 예시 (x 값을 디폴트로 100) val AdderX100 = AdderDefaultX(100) // 예시 (y 값을 디폴트로 100) val AdderY100 = AdderDefaultY(100)- 함수는 불려질 때 어떤 파라미터가 어떤 값이 되어야 함
- 주석/문서화보다 더 좋음! => 안정성 증가