[scala] 형식 안전 열거 형 형식을 모델링하는 방법은 무엇입니까?

스칼라에는 enumJava와 같은 유형 안전 기능이 없습니다 . 관련 상수 세트가 주어지면 스칼라에서 이러한 상수를 나타내는 가장 좋은 방법은 무엇입니까?



답변

http://www.scala-lang.org/docu/files/api/scala/Enumeration.html

사용 예

  object Main extends App {

    object WeekDay extends Enumeration {
      type WeekDay = Value
      val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
    }
    import WeekDay._

    def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun)

    WeekDay.values filter isWorkingDay foreach println
  }


답변

나는 예라고한다 스칼라 문서 밖으로 복사 에 의해 skaffman은 위의 연습에 제한 유틸리티입니다 (당신 수도뿐만 아니라 사용 case object의).

Java와 가장 유사한 것을 얻으려면 Enum(예를 들어 현명 toString하고 valueOf메소드가있는-아마도 enum 값을 데이터베이스에 유지하는 경우) 약간 수정해야합니다. skaffman 의 코드를 사용한 경우 :

WeekDay.valueOf("Sun") //returns None
WeekDay.Tue.toString   //returns Weekday(2)

다음 선언을 사용하는 반면 :

object WeekDay extends Enumeration {
  type WeekDay = Value
  val Mon = Value("Mon")
  val Tue = Value("Tue")
  ... etc
}

더 합리적인 결과를 얻습니다.

WeekDay.valueOf("Sun") //returns Some(Sun)
WeekDay.Tue.toString   //returns Tue


답변

여러 가지 방법이 있습니다.

1) 기호를 사용하십시오. 그러나 기호가 필요한 기호가 아닌 기호를 허용하지 않는 한 유형 안전성을 제공하지는 않습니다. 나는 여기서 완전성을 위해 언급하고 있습니다. 사용 예는 다음과 같습니다.

def update(what: Symbol, where: Int, newValue: Array[Int]): MatrixInt =
  what match {
    case 'row => replaceRow(where, newValue)
    case 'col | 'column => replaceCol(where, newValue)
    case _ => throw new IllegalArgumentException
  }

// At REPL:   
scala> val a = unitMatrixInt(3)
a: teste7.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 0 1 /

scala> a('row, 1) = a.row(0)
res41: teste7.MatrixInt =
/ 1 0 0 \
| 1 0 0 |
\ 0 0 1 /

scala> a('column, 2) = a.row(0)
res42: teste7.MatrixInt =
/ 1 0 1 \
| 0 1 0 |
\ 0 0 0 /

2) 수업 이용 Enumeration:

object Dimension extends Enumeration {
  type Dimension = Value
  val Row, Column = Value
}

또는 직렬화하거나 표시해야하는 경우 :

object Dimension extends Enumeration("Row", "Column") {
  type Dimension = Value
  val Row, Column = Value
}

이것은 다음과 같이 사용될 수 있습니다 :

def update(what: Dimension, where: Int, newValue: Array[Int]): MatrixInt =
  what match {
    case Row => replaceRow(where, newValue)
    case Column => replaceCol(where, newValue)
  }

// At REPL:
scala> a(Row, 2) = a.row(1)
<console>:13: error: not found: value Row
       a(Row, 2) = a.row(1)
         ^

scala> a(Dimension.Row, 2) = a.row(1)
res1: teste.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 1 0 /

scala> import Dimension._
import Dimension._

scala> a(Row, 2) = a.row(1)
res2: teste.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 1 0 /

불행히도, 모든 경기가 설명되는 것은 아닙니다. 일치하는 행이나 열을 잊어 버린 경우 스칼라 컴파일러는 경고하지 않았을 것입니다. 그래서 그것은 나에게 약간의 유형의 안전을하지만만큼 얻을 수 없습니다.

3) 케이스 객체 :

sealed abstract class Dimension
case object Row extends Dimension
case object Column extends Dimension

이제에 사례를 남기지 않으면 match컴파일러에서 경고합니다.

MatrixInt.scala:70: warning: match is not exhaustive!
missing combination         Column

    what match {
    ^
one warning found

그것은 거의 같은 방식으로 사용되며 심지어 필요하지 않습니다 import:

scala> val a = unitMatrixInt(3)
a: teste3.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 0 0 1 /

scala> a(Row,2) = a.row(0)
res15: teste3.MatrixInt =
/ 1 0 0 \
| 0 1 0 |
\ 1 0 0 /

그렇다면 왜 케이스 객체 대신 Enumeration을 사용하는지 궁금 할 것입니다. 실제로 케이스 객체는 여기와 같이 여러 번 장점이 있습니다. 그러나 Enumeration 클래스에는 반복자,지도, flatMap, 필터 등을 반환하는 요소 (Scala 2.8의 반복자)와 같은 많은 Collection 메소드가 있습니다.

이 답변은 본질적으로 내 블로그 의이 기사 에서 선택한 부분입니다 .


답변

명명 된 열거를 선언하는 약간 덜 장황한 방법 :

object WeekDay extends Enumeration("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") {
  type WeekDay = Value
  val Sun, Mon, Tue, Wed, Thu, Fri, Sat = Value
}

WeekDay.valueOf("Wed") // returns Some(Wed)
WeekDay.Fri.toString   // returns Fri

물론 여기서 문제는 이름과 val의 순서를 동기화하여 유지해야한다는 것인데, name과 val이 같은 줄에 선언되어 있으면 더 쉽습니다.


답변

열거 대신 봉인 된 추상 클래스를 사용할 수 있습니다. 예를 들면 다음과 같습니다.

sealed abstract class Constraint(val name: String, val verifier: Int => Boolean)

case object NotTooBig extends Constraint("NotTooBig", (_ < 1000))
case object NonZero extends Constraint("NonZero", (_ != 0))
case class NotEquals(x: Int) extends Constraint("NotEquals " + x, (_ != x))

object Main {

  def eval(ctrs: Seq[Constraint])(x: Int): Boolean =
    (true /: ctrs){ case (accum, ctr) => accum && ctr.verifier(x) }

  def main(args: Array[String]) {
    val ctrs = NotTooBig :: NotEquals(5) :: Nil
    val evaluate = eval(ctrs) _

    println(evaluate(3000))
    println(evaluate(3))
    println(evaluate(5))
  }

}


답변

방금 enumeratum을 발견했습니다 . 그것은 꽤 놀랍고 똑같이 놀랍습니다. 더 잘 알려져 있지 않습니다!


답변

스칼라의 “enumerations”에 관한 모든 옵션에 대한 광범위한 연구를 한 후,이 도메인에 대한 훨씬 더 완전한 개요를 다른 StackOverflow 스레드 에 게시했습니다 . 여기에는 JVM 클래스 / 개체 초기화 순서 문제를 해결 한 “밀봉 된 특성 + 사례 개체”패턴에 대한 솔루션이 포함되어 있습니다.