코세라 Scala 강의 중 MergeSort 예제에서 조금 이해하기 쉽게 분리한 예제이다.


https://www.coursera.org/learn/progfun1/lecture/0uFfe/lecture-5-2-pairs-and-tuples

object MergeSort {
def merge(left: List[Int], right: List[Int]): List[Int] = (left, right) match {
case (l, Nil) => l
case (Nil, r) => r
case (leftHead :: leftTail, rightHead :: rightTail) =>
if (leftHead < rightHead) leftHead::merge(leftTail, right)
else rightHead :: merge(left, rightTail)
}

def mergeSort(list: List[Int]): List[Int] = {
val n = list.length / 2
if (n == 0) list
else {
val (left, right) = list splitAt n
merge(mergeSort(left), mergeSort(right))
}
}
}

결과


scala> MergeSort.mergeSort(List())

res17: List[Int] = List()


scala> MergeSort.mergeSort(List(100,50,120,19))

res16: List[Int] = List(19, 50, 100, 120)





Int 타입을 T 타입으로 (order:(T, T) => scala.Boolean)을 추가해서 compare를 할 수 있게 한다.


object MergeSort {
def mergeSort[T](list: List[T])(order: (T, T) => scala.Boolean): List[T] = {
val n = list.length / 2
if (n == 0) list
else {
def merge(left: List[T], right: List[T]) : List[T] = (left, right) match {
case (l, Nil) => l
case (Nil, r) => r
case (leftHead :: leftTail, rightHead :: rightTail) =>
if (order(leftHead, rightHead)) leftHead::merge(leftTail, right)
else rightHead :: merge(left, rightTail)
}
val (left, right) = list splitAt n
merge(mergeSort(left)(order), mergeSort(right)(order))
}
}
}


object Main extends App {
println(MergeSort.mergeSort(List(100,50,120,19))((x: Int, y: Int) => (x < y)))
println(MergeSort.mergeSort(List(100,50,120,19))((x: Int, y: Int) => (x > y)))
println(MergeSort.mergeSort(List("samuel","jack","juno","ben"))((x: String, y: String) => (x.compareTo(y) < 0)))
println(MergeSort.mergeSort(List("samuel","jack","juno","ben"))((x: String, y: String) => (x.compareTo(y) > 0)))
}


결과는 다음과 같다.


List(19, 50, 100, 120)

List(120, 100, 50, 19)




언제나 스칼라는 타입을 숨길 수 있다.


List(100,50,120,19))((x, y) => (x > y))





여기에 math.Ordering을 임포트해본다.

import math.Ordering
object MergeSort {
def mergeSort[T](list: List[T])(order: Ordering[T]): List[T] = {
val n = list.length / 2
if (n == 0) list
else {
def merge(left: List[T], right: List[T]) : List[T] = (left, right) match {
case (l, Nil) => l
case (Nil, r) => r
case (leftHead :: leftTail, rightHead :: rightTail) =>
if (order.lt(leftHead, rightHead)) leftHead::merge(leftTail, right)
else rightHead :: merge(left, rightTail)
}
val (left, right) = list splitAt n
merge(mergeSort(left)(order), mergeSort(right)(order))
}
}
}

object Main extends App {
println(MergeSort.mergeSort(List(100,50,120,19))(Ordering.Int))
println(MergeSort.mergeSort(List(100,50,120,19))(Ordering.Int))
println(MergeSort.mergeSort(List("samuel","jack","juno","ben"))(Ordering.String))
println(MergeSort.mergeSort(List("samuel","jack","juno","ben"))(Ordering.String))
}


결과는 이전과 동일하다. 






여기에 implicit을 추가해본다. 그리고 implicit을 쓰면 적당히 코드 삭제가 가능하다.




import math.Ordering
object MergeSort {
def mergeSort[T](list: List[T])(implicit order: Ordering[T]): List[T] = {
val n = list.length / 2
if (n == 0) list
else {
def merge(left: List[T], right: List[T]) : List[T] = (left, right) match {
case (l, Nil) => l
case (Nil, r) => r
case (leftHead :: leftTail, rightHead :: rightTail) =>
if (order.lt(leftHead, rightHead)) leftHead::merge(leftTail, right)
else rightHead :: merge(left, rightTail)
}
val (left, right) = list splitAt n
merge(mergeSort(left), mergeSort(right))
}
}
}

object Main extends App {
println(MergeSort.mergeSort(List(100,50,120,19)))
println(MergeSort.mergeSort(List(100,50,120,19)))
println(MergeSort.mergeSort(List("samuel","jack","juno","ben")))
println(MergeSort.mergeSort(List("samuel","jack","juno","ben")))
}

역시 결과가 동일하다.





Posted by '김용환'
,


function value의 확장은 스칼라의 기초 지식이다. 


https://twitter.github.io/scala_school/ko/basics2.html



apply 개념은 다음 예제를 살펴본다. 



class IntMultiply {

  def apply(t1: Int, t2: Int): Int = (t1*t2)

  def apply() = new IntMultiply

}


object IntMultiply extends App {

  val multiply = new IntMultiply()

  val s = multiply(5,2)

  println(s)

}





object IntMultiply extends App {

  def apply(t1: Int, t2: Int): Int = (t1*t2)

}


class IntMultiply {

  IntMultiply(3,2)

}







아래 익명 함수(function value)는 아래 익명 클래스로 확장(expand)된다.


scala> (x : Int) => x * x

res1: Int => Int = $$Lambda$1034/1553646796@4df39a88




익명 클래스 1)

scala> {
     | class AnonFun extends Function1[Int, Int] {
     | def apply(x: Int) = x * x
     | }
     | new AnonFun
     | }
res3: Int => Int = <function1>


또는 

익명 클래스 2) 

scala> new Function1[Int, Int] {

     | def apply(x: Int) = x * x

     | }

res4: Int => Int = <function1>



따라서 List(1,2)는 List.apply(1,2)가 호출된다.



아래와 같은 함수가 있다고 가정하면..

scala> def f(x: Int): Boolean = ???
f: (x: Int)Boolean

function value는 다음과 같이 사용할 수 있다. 

scala> (x: Int) => f(x)
res5: Int => Boolean = $$Lambda$1110/1709317347@6c3659be

클래스로 확장되면 다음과 같다.

scala> new Function1[Int, Boolean] {
     | def apply(x : Int) = f(x)
     | }
res6: Int => Boolean = <function1>




Posted by '김용환'
,




coursera의 강의 Functional Programming Principles in Scala 4장 강의 중에 Funtional 언어의 특징을 유난히 보였던 예제가 있어서 끄적거려 봤다..


abstract class Boolean {
def ifThenElse[T](t: => T, e: => T): T

def unary_! : Boolean = ifThenElse(False , True)

def && (x: => Boolean): Boolean = ifThenElse(x, False)
def || (x: => Boolean): Boolean = ifThenElse(True, x)

def == (x: Boolean): Boolean = ifThenElse(x, x.unary_!)
def != (x: Boolean): Boolean = ifThenElse(x.unary_! , x)

def < (x: Boolean): Boolean = ifThenElse(False, x)
def > (x: Boolean): Boolean = ifThenElse(x, False)

}



object False extends Boolean {
def ifThenElse[T](t: => T, e: => T) = e
}


object True extends Boolean {
def ifThenElse[T](t: => T, e: => T) = t
}



테스트 결과


True.ifThenElse(println("yes"), println("no")) // yes
False.ifThenElse(println("yes"), println("no")) // no
(! True).ifThenElse(println("yes"), println("no")) // no
(! False).ifThenElse(println("yes"), println("no")) // yes

println
(True && False).ifThenElse(println("yes"), println("no")) // no
(True && True).ifThenElse(println("yes"), println("no")) // yes
(False && True).ifThenElse(println("yes"), println("no")) // no
(False && False).ifThenElse(println("yes"), println("no")) // no

println
(True == True).ifThenElse(println("yes"), println("no")) // yes
(True && True == True).ifThenElse(println("yes"), println("no")) //yes
(False || True != True).ifThenElse(println("yes"), println("no")) //no

println
(False < True).ifThenElse(println("yes"), println("no")) // yes
(False > True).ifThenElse(println("yes"), println("no")) // no



Posted by '김용환'
,


sbt about와 sbt -v 둘 다 사용해 버전을 확인할 수 있다. 


$ sbt about

[info] Set current project to scala (in build file:/Users/samuel.kim/dev/scala/)

[info] This is sbt 0.13.12

[info] The current project is {file:/Users/samuel.kim/dev/scala/}scala 0.1-SNAPSHOT

[info] The current project is built against Scala 2.10.6

[info] Available Plugins: sbt.plugins.IvyPlugin, sbt.plugins.JvmPlugin, sbt.plugins.CorePlugin, sbt.plugins.JUnitXmlReportPlugin

[info] sbt, sbt plugins, and build definitions are using Scala 2.10.6





$ sbt -v

[process_args] java_version = '1.8.0_101'

# Executing command line:

java

-Xms1024m

-Xmx1024m

-XX:ReservedCodeCacheSize=128m

-XX:MaxMetaspaceSize=256m

-jar

/usr/local/Cellar/sbt/0.13.12/libexec/sbt-launch.jar


Posted by '김용환'
,

Scala For Data Science 책에 소개된 json->객체, 객체 ->json 예제로서 내용이 좋아서 조금 수정해봤다.



Repo.scala


package models


case class Repo(

  val name: String,

  val language: String,

  val size: Long

)




Api.scala

package controllers


import models.Repo

import play.api.libs.json._

import play.api.mvc._


class Api extends Controller {

  implicit val writeRepos = new Writes[Repo] {

    override def writes(repo: Repo) = Json.obj(

      "name" -> repo.name,

      "language" -> repo.language,

      "size" -> repo.size

    )

  }


  val data = List[Repo](

    Repo("backend", "scala", 1),

    Repo("frontend", "jquery", 2)

  )


  def repos(username: String) = Action {

    val repoArray = Json.toJson(data)

    Ok(repoArray)

  }

}



conf/routes 파일에 다음을 추가한다.

GET     /api/repos/:username        controllers.Api.repos(username)



웹 브라우져에서 http://127.0.0.1:9000/api/repos/odersky을 호출한다. 



결과


[{"name":"backend","language":"scala","size":1},{"name":"frontend","language":"jquery","size":2}]




Play 프레임워크에서는 Writes[T] 타입 클래스는 단 하나의 메소드를 가진다.


trait Writes[T] {

  def writes(obj:T):Json

}


Writes 메소드는 Play 프레임워크에 내장되어 있어서 Writes를 직접 구현하지 않아도 된다.


Writes[Repo] 인스턴스는 Api controller 코드에서 정의해서 Api controller 내부에서 사용할 수 있도록 한다. 

Writes[Repo] 타입 클래스는 Repo 인스턴스를 JSON으로 변환하는 방법을 안다. JSON HTTP 응답을 생성하는 방법을 알고 있기 때문에 깔끔한 코드를 짤 수 있다.


자바라면 맵/배열을 이용하거나 custom Serializer 를 사용해야 한다(예, http://www.baeldung.com/jackson-map)


이를 편하게 만들어준다고 할 수 있는 큰 기능인 것 같다.




이제는 json을 객체로 변환하는 코드 테스트이다. 


controllers.Api 클래스의 코드를 다음과 같이 변경한다.




package controllers


import models.Repo

import play.api.mvc._

import play.api.libs.ws.WS

import play.api.Play.current

import play.api.libs.json._

import play.api.libs.functional.syntax._

import play.api.libs.concurrent.Execution.Implicits.defaultContext


class Api extends Controller {

  // Repo -> json

  implicit val writeRepos = new Writes[Repo] {

    override def writes(repo: Repo) = Json.obj(

      "name" -> repo.name,

      "language" -> repo.language,

      "size" -> repo.size

    )

  }


  // json -> Repo

  implicit val default:Reads[Repo] = (

    (JsPath \ "name").read[String] and

      (JsPath \ "language").read[String] and

      (JsPath \ "size").read[Long]

    )(Repo.apply _)


  val data = List[Repo](

    Repo("backend", "scala", 1),

    Repo("frontend", "jquery", 2)

  )


  def repos(username:String) = Action.async {


    val url = s"https://api.github.com/users/$username/repos"

    val response = WS.url(url).get()


    response.map { r =>

      if (r.status == 200) {

        val reposOpt = Json.parse(r.body).validate[List[Repo]]

        reposOpt match {

          case JsSuccess(repos, _) => Ok(Json.toJson(repos))

          case _ => InternalServerError

        }

      }

      else {

        NotFound

      }

    }

  }

}



https://api.github.com/users/odersky/repos의 코드를 실행해 json 파싱하고 Read[Repo]타입 클래스에 저장하도록 한다. 




Posted by '김용환'
,


spark streaming job을 개발할 때,


streaming이 잘 동작하는데. 문제 없는지 확인하고 싶을 때  


다음 URL을 잘 이용해 본다. spark ui를 



https://docs.cloud.databricks.com/docs/latest/databricks_guide/07%20Spark%20Streaming/02%20Debugging%20Spark%20Streaming%20Application.html




'scala' 카테고리의 다른 글

[sbt] sbt 버전 확인하기  (0) 2017.06.26
[play2] json-객체 연동 코드 예제  (0) 2017.06.26
[spark] parquet 사용 예제  (0) 2017.05.26
[spark] zipWithIndex, for-yield 예제  (0) 2017.05.25
[spark] join 예제  (0) 2017.05.23
Posted by '김용환'
,


parquet는 성능이 좋은 것으로 알려져 있지만, 일반 텍스트로 볼 수 없다는 단점이 있다..


그러나 기능적으로 봤을 overwrite를 할 수 있다는 점에서.. parquet가 많이 쓰여질 것 같다.




로컬 장비에서 parquet 테스트는 다음처럼 진행 할 수 있다. 



scala> val ds = Seq(1, 2, 3, 4, 5).toDS

ds: org.apache.spark.sql.Dataset[Int] = [value: int]


scala> ds.write.parquet("/usr/local/spark-2.1.0-bin-hadoop2.7/test1")

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".

SLF4J: Defaulting to no-operation (NOP) logger implementation

SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.


scala> val fromParquet = spark.read.parquet("/usr/local/spark-2.1.0-bin-hadoop2.7/test1")

fromParquet: org.apache.spark.sql.DataFrame = [value: int]


scala> fromParquet

res2: org.apache.spark.sql.DataFrame = [value: int]


scala> fromParquet.show

+-----+

|value|

+-----+

|    1|

|    2|

|    3|

|    4|

|    5|

+-----+



Posted by '김용환'
,


이 코드는 zipWithIndex와 for-yield 문, if문을 잘 설명하는 코드이다. 


scala> def occurrencesOf[A](elem:A, collection:List[A]):List[Int] = {

     |   for {

     |     (currentElem, index) <- collection.zipWithIndex

     |     if (currentElem == elem)

     |   } yield index

     | }

occurrencesOf: [A](elem: A, collection: List[A])List[Int]


scala>


scala> occurrencesOf(10, List(0,1,2,3,4,10))

res0: List[Int] = List(5)




occurrencesOf(10, List(0,1,2,3,4,10)) 코드를 설명한다.


collection.zipWithIndex 은 다음과 같은 값을 리턴한다.


((List(0), 0), (List(1), 1), (List(2), 2), (List(3), 3), (List(4), 4), (List(10), 10))


그러다다 elem으로 들어온 10 값을 만나면 관련 index 값 10을 리턴한다. 






'scala' 카테고리의 다른 글

[spark] spark streaming job 개발시 유익한 싸이트  (0) 2017.06.07
[spark] parquet 사용 예제  (0) 2017.05.26
[spark] join 예제  (0) 2017.05.23
[spark] where과 filter의 차이  (0) 2017.05.23
[spark2] spark SQL 예제  (0) 2017.05.20
Posted by '김용환'
,

[spark] join 예제

scala 2017. 5. 23. 14:56


spark의 join 예제이다. 


sql의 join과 같은 개념이다. collection과 case class를 활용해 데이터 집합을 하나로 결합할 수 있다. 





scala>     val person = sc.parallelize(Array((1, "samuel"), (2, "jackson"), (3, "redis"))).toDF("number", "name")

person: org.apache.spark.sql.DataFrame = [number: int, name: string]


scala> val address = sc.parallelize(Array(("samuel", "seoul"), ("jackson", "new york"), ("juno", "iceland"))).toDF("name", "address")

address: org.apache.spark.sql.DataFrame = [name: string, address: string]


scala> person.show

+------+-------+

|number|   name|

+------+-------+

|     1| samuel|

|     2|jackson|

|     3|  redis|

+------+-------+


scala> person.join(address, "name").show

+-------+------+--------+

|   name|number| address|

+-------+------+--------+

|jackson|     2|new york|

| samuel|     1|   seoul|

+-------+------+--------+





그러나 여러 join타입(예, left_outer)을 넣으면 에러가 발생한다.

 scala> person.join(address, "name", "left_outer").show
<console>:29: error: overloaded method value join with alternatives:
  (right: org.apache.spark.sql.Dataset[_],joinExprs: org.apache.spark.sql.Column,joinType: String)org.apache.spark.sql.DataFrame <and>
  (right: org.apache.spark.sql.Dataset[_],usingColumns: Seq[String],joinType: String)org.apache.spark.sql.DataFrame
 cannot be applied to (org.apache.spark.sql.DataFrame, String, String)
       person.join(address, "name", "left_outer").show
                    ^



"name" 대신 Seq("name")을 사용한다.

 
scala> person.join(address, Seq("name"), "inner").show
+-------+------+--------+
|   name|number| address|
+-------+------+--------+
|jackson|     2|new york|
| samuel|     1|   seoul|
+-------+------+--------+



scala> person.join(address, Seq("name"), "left_outer").show
+-------+------+--------+
|   name|number| address|
+-------+------+--------+
|jackson|     2|new york|
| samuel|     1|   seoul|
|  redis|     3|    null|
+-------+------+--------+



case class를 이용할 수도 있다.



scala>     val sqlContext = new SQLContext(sc)

warning: there was one deprecation warning; re-run with -deprecation for details

sqlContext: org.apache.spark.sql.SQLContext = org.apache.spark.sql.SQLContext@2418ffcc


scala>     case class Person(number: Int, name: String)

defined class Person


scala>     case class Address(name: String, address: String)

defined class Address


scala>     val person = sqlContext.createDataFrame(Person(1, "samuel") :: Person(2, "jackson") :: Person(3, "redis") :: Nil).as("person_dataframe")

person: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [number: int, name: string]


scala>     val address = sqlContext.createDataFrame(Address("samuel", "seoul") :: Address("jackson", "new york") :: Address("juno", "iceland") :: Nil).as("address_dataframe")

address: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [name: string, address: string]


scala> val joined_dataframe = person.join(address, col("person_dataframe.name") === col("address_dataframe.name"), "inner")

joined_dataframe: org.apache.spark.sql.DataFrame = [number: int, name: string ... 2 more fields]

###아래 처럼 사용할 수도 있다.

scala> val joined_dataframe = person.join(address, $"person_dataframe.name" === $"address_dataframe.name", "inner")

joined_dataframe: org.apache.spark.sql.DataFrame = [number: int, name: string ... 2 more fields]



scala> joined_dataframe.show

+------+-------+-------+--------+

|number|   name|   name| address|

+------+-------+-------+--------+

|     1| samuel| samuel|   seoul|

|     2|jackson|jackson|new york|

+------+-------+-------+--------+







만약 필드 중에 null 컬럼 데이터가 있다면, Option을 사용하는 것이 좋은 방법일 것이다.

'scala' 카테고리의 다른 글

[spark] parquet 사용 예제  (0) 2017.05.26
[spark] zipWithIndex, for-yield 예제  (0) 2017.05.25
[spark] where과 filter의 차이  (0) 2017.05.23
[spark2] spark SQL 예제  (0) 2017.05.20
[spark2] spark2 rdd 생성 -makeRDD  (0) 2017.04.29
Posted by '김용환'
,



filter는 dataframe에서 where를 spark sql에서 사용하는데, 

이 둘의 차이가 무엇일까 살펴봤더니..


where는 filter의 앨리어스라 한다.




https://spark.apache.org/docs/1.5.2/api/scala/index.html#org.apache.spark.sql.DataFrame


defwhere(condition: Column)DataFrame

Filters rows using the given condition. This is an alias for filter.




결국은 아래 함수의 결과는 동일하다.


employee.filter($"age" > 15)

employee.where($"age" > 15)

'scala' 카테고리의 다른 글

[spark] zipWithIndex, for-yield 예제  (0) 2017.05.25
[spark] join 예제  (0) 2017.05.23
[spark2] spark SQL 예제  (0) 2017.05.20
[spark2] spark2 rdd 생성 -makeRDD  (0) 2017.04.29
[scala] 라인 피드("\n") 관련 예시 코드  (0) 2017.04.24
Posted by '김용환'
,