[FIXED] Wie kann ich vermeiden, einen Typ zu duplizieren, der in einer Objektklasse in Scala gebunden ist?

Ausgabe

Ja, ich habe die sehr ähnlich betitelte Frage überprüft, aber die gegebene Antwort ist für mich nicht so hilfreich, da ich neu bei Scala bin und Probleme habe, sie zu verstehen.

Ich schreibe einige Funktionen, die eine Liste von Karten überprüfen und basierend auf dem Ergebnis der Liste eine Punktzahl zurückgeben. Technisch gesehen überprüft es eine Liste von Kartengruppen, aber ich vereinfache den Code für die Zwecke dieser Frage.

Nun möchte ich, dass diese Funktionen auf verschiedene Arten von Scoring erweiterbar sind. Wenn zum Beispiel alle Karten Herzen sind, können wir ihnen 1 Punkt geben. In einem anderen Regelsatz kann es jedoch 3 Punkte geben.

Ich habe einen Punkte-Wrapper, der zu einem Endergebnis führt. Dies bedeutet, dass eine Art von Punkten zu einem anderen Endergebnis führen kann als eine andere Art von Punkten. Der Zweck hier ist, Ihnen zu ermöglichen, die Wertung anzupassen und das Kartenspiel auf eine etwas andere Art und Weise zu spielen.

Sie werden im folgenden Beispielcode sehen, aber am Ende bekomme ich viele Wiederholungen in meinen Methodendeklarationen, nämlich [T <: HandPoints[T]]immer wieder schreiben zu müssen.

Alle defMethoden wurden in eine geschrieben object, daher kann ich den Typparameter nicht zur Klasse hinzufügen.

Ich stelle mir vor, dass es wahrscheinlich eine nette Lösung gibt, diese Methoden außerhalb der Klasse zu extrahieren, aber ich möchte, dass die Methoden, die die Karten überprüfen, nicht wiederholt werden, daher macht es für mich sehr viel Sinn, sie statisch in einer zu deklarierenobject

Hier ist das HandPoints-Merkmal:

trait HandPoints[T] {
  def toHandScore: HandScore
  def zero: T
  def add(that: T): T
}

case class RegularPoint(points: Int) extends HandPoints[RegularPoint] {
  override def toHandScore: HandScore = HandScore(points)
  override def zero: RegularPoint = RegularPoint(0)
  override def add(that: RegularPoint): RegularPoint = RegularPoint(points + that.points)
}

case class DoublingPoints(points: Int) extends HandPoints[DoublingPoints] {
  override def toHandScore: HandScore = HandScore(points*2)
  override def zero: DoublingPoints = DoublingPoints(0)
  override def add(that: DoublingPoints): DoublingPoints = DoublingPoints(points + that.points)
}

case class HandScore(score: Int) {

}

Hier sind die Funktionen, die ich geschrieben habe, um die Karten zu bewerten

  
  trait Card {
    def getValue: Int
    def getSuit: String
  }


  def scored[T <: HandPoints[T]](score: T)(boolean: Boolean): T = {
    if (boolean) score else score.zero
  }

  def isAllEvens[T <: HandPoints[T]](score: T)(cards: List[Card]): T = {
    scored(score) {
      cards.forall(_.getValue % 2 == 0)
    }
  }

  def isAllReds[T <: HandPoints[T]](score: T)(cards: List[Card]): T = {
    scored(score) {
      cards.forall(List("HEARTS", "DIAMONDS").contains(_))
    }
  }

  def isAllNoDuplicates[T <: HandPoints[T]](score: T)(cards: List[Card]): T = {
    scored(score) {
      cards.distinct == cards
    }
  }
  
  val regularGameCriteria: List[List[Card] => RegularPoint] = List(
    isAllEvens(RegularPoint(1)),
    isAllReds(RegularPoint(3)),
    isAllNoDuplicates(RegularPoint(5))
  )
  
  val beginnerGameCriteria: List[List[Card] => RegularPoint] = List(
    isAllEvens(RegularPoint(1)),
    isAllReds(RegularPoint(1)),
    isAllNoDuplicates(RegularPoint(1))
  )

  val superGameCriteria: List[List[Card] => DoublingPoints] = List(
    isAllEvens(DoublingPoints(1)),
    isAllReds(DoublingPoints(3)),
    isAllNoDuplicates(DoublingPoints(5))
  )
  
  def countScore[T <: HandPoints[T]](scoreList: List[List[Card] => T])(melds: List[Card]): T = {
    scoreList.map(f => f(melds)).reduce((a, b) => a.add(b))
  }

  def regularGameScore(cards: List[Card]): RegularPoint = {
    countScore(regularGameCriteria)(cards)
  }

  def beginnerGameScore(cards: List[Card]): RegularPoint = {
    countScore(beginnerGameCriteria)(cards)
  }

  def superGameScore(cards: List[Card]): DoublingPoints = {
    countScore(superGameCriteria)(cards)
  }

Lösung

Zunächst können Sie sich ansehen, wie F-begrenzter Polymorphismus durch Ad-hoc-Polymorphismus (Typklassen) ersetzt werden kann:

https://tpolecat.github.io/2015/04/29/f-bounds.html

Zweitens sind die Grenzen [T <: HandPoints[T]]in verschiedenen Methoden eigentlich keine Codeduplizierung. Unterschiedlich Tin verschiedenen Methoden sind verschiedene Typparameter. Sie haben sie gerade mit demselben Brief angerufen. Grenzen für einen Typparameter schränken einen anderen Typparameter nicht ein.

Ich bin neugierig, warum Sie eine Codeduplizierung [T <: HandPoints[T]]in verschiedenen Methoden in Betracht ziehen und nicht (score: T)oder (cards: List[Card]). Ich schätze, weil die meisten Leute über Begriffe nachdenken und Typen für weniger wichtig halten.

Drittens sollten Sie damit beginnen, OOP (oder FP mit Typklassen oder einigen ihrer Mischungen) zu erkunden, dh Ihre Methoden in Klassen/Objekten (oder Typklassen) mit einem bestimmten Verhalten zu organisieren. Jetzt sieht eine Reihe von (statischen) Methoden wie prozedurale Programmierung aus.

Zum Beispiel können wir für den Anfang zwei Klassen einführen HandPointsHandlerund Criteria(Sie können bessere Namen aufgreifen):

case class HandScore(score: Int)

trait HandPoints[T] {
  def toHandScore: HandScore
  def zero: T
  def add(that: T): T
}

case class RegularPoint(points: Int) extends HandPoints[RegularPoint] {
  override def toHandScore: HandScore = HandScore(points)
  override def zero: RegularPoint = RegularPoint(0)
  override def add(that: RegularPoint): RegularPoint = RegularPoint(points + that.points)
}

case class DoublingPoints(points: Int) extends HandPoints[DoublingPoints] {
  override def toHandScore: HandScore = HandScore(points*2)
  override def zero: DoublingPoints = DoublingPoints(0)
  override def add(that: DoublingPoints): DoublingPoints = DoublingPoints(points + that.points)
}

trait Card {
  def getValue: Int
  def getSuit: String
}

// new class
class HandPointsHandler[T <: HandPoints[T]] {
  def scored(score: T)(boolean: Boolean): T =
    if (boolean) score else score.zero

  def isAllEvens(score: T)(cards: List[Card]): T =
    scored(score) {
      cards.forall(_.getValue % 2 == 0)
    }

  def isAllReds(score: T)(cards: List[Card]): T =
    scored(score) {
      cards.forall(List("HEARTS", "DIAMONDS").contains)
    }

  def isAllNoDuplicates(score: T)(cards: List[Card]): T =
    scored(score) {
      cards.distinct == cards
    }

  def countScore(scoreList: List[List[Card] => T])(melds: List[Card]): T =
    scoreList.map(_.apply(melds)).reduce(_ add _)
}

// new class
class Criteria[T <: HandPoints[T]](handler: HandPointsHandler[T], points: List[T]) {
  val gameCriteria: List[List[Card] => T] = {
    List(
      handler.isAllEvens _,
      handler.isAllReds _,
      handler.isAllNoDuplicates _
    ).zip(points).map { case (f, point) => f(point) }
  }
}

val points135 = List(1, 3, 5)
val points111 = List(1, 1, 1)

val regularPointsHandler = new HandPointsHandler[RegularPoint]

val regularGameCriteria: List[List[Card] => RegularPoint] =
  new Criteria[RegularPoint](regularPointsHandler, points135.map(RegularPoint)).gameCriteria

val beginnerGameCriteria: List[List[Card] => RegularPoint] =
  new Criteria[RegularPoint](regularPointsHandler, points111.map(RegularPoint)).gameCriteria

val doublingPointsHandler = new HandPointsHandler[DoublingPoints]

val superGameCriteria: List[List[Card] => DoublingPoints] =
  new Criteria[DoublingPoints](doublingPointsHandler, points135.map(DoublingPoints)).gameCriteria

def regularGameScore(cards: List[Card]): RegularPoint =
  regularPointsHandler.countScore(regularGameCriteria)(cards)

def beginnerGameScore(cards: List[Card]): RegularPoint =
  regularPointsHandler.countScore(beginnerGameCriteria)(cards)

def superGameScore(cards: List[Card]): DoublingPoints =
  doublingPointsHandler.countScore(superGameCriteria)(cards)


Beantwortet von –
Dmytro Mitin


Antwort geprüft von –
Candace Johnson (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like