Плохо написанный Scala-код. Как бы выглядел хорошо написанный? (игра с парадигмами и техниками программирования) - PullRequest
0 голосов
/ 28 июня 2018

Я новичок в Scala. Следующий код является моим продолжением первого урока из курса «Принципы функционального программирования в Scala» профессора Мартина Одерского из первого урока о Rationals «Функции и данные».

Я добавил opti метод, который делит знаменатель и знаменатель на их наибольший общий делитель (gcd), например, делает 2/4 равным 1/2. Но ради интереса я решил добавить возможность добавления автоматической оптимизации после каждого вызова add и Sub (на этот раз забудьте о производительности). Поэтому я могу написать метод makeOptiAuto(Boolean): Unit, но для этого потребуется оператор if в каждом методе and / sub. Поэтому я решил создать класс с именем OptimalizedRational, который будет вызывать opti в конце каждого and / sub вызов.

Проблема в том, что я сделал это как расширение моего класса Rational. Может быть, это должен быть его член (Rational.OptimaliyedRational)? Во-вторых, я должен вызвать конструктор Rational. Это необходимо в этом случае? На самом деле, я переопределяю их, поэтому конструктор Rational бесполезен. Я могу назвать gcd как extends Rational(gcd(x, y) / x, gcd(x, y) / y), но это то же самое значение. Так что я должен использовать val. Можно ли вызвать Rational конструктор, используя gcd только один раз? Как лучше всего выполнять такую ​​работу? Другая проблема заключается в add / sub методах. Если я хочу вызвать метод с помощью super, то мне возвращается объект Rational, поэтому, если я хочу вернуть OptimalizedRational, мне нужно снова использовать optiAuto ... Может быть, я должен использовать asInstanceOf[Rational] для его приведения, но сначала он не работает (выдается ошибка), а во-вторых, он медленный (Java выполняет динамическое приведение, если я не ошибаюсь).

Итак, кто будет смотреть хорошо написанный код Scala, который делает то, что я пытался сделать? Может кто-то отправил исправленную версию с некоторыми пояснениями?

код:

object Learning {

// IMMPLEMENTATION OF `Rational` CLASS:
class Rational(x: Int, y: Int) {
  def nom = x
  def denom = y

  def add(that: Rational): Rational =
    new Rational((nom*that.denom + that.nom*denom), (denom*that.denom))

  def neg: Rational =
    new Rational(-nom, denom)

  def sub(that: Rational): Rational =
    add(that.neg)

    // used by opti() and for OptimalizedRational       
    protected def gcd(a: Int, b: Int): Int =
        if (b == 0) a
        else gcd(b, a % b)

  def opti(): Rational = {
    val d = gcd(nom, denom)
    new Rational(nom / d, denom / d)
  }




  // shorthand methods: 
  def add(a: Int, b: Int): Rational = add(new Rational(a, b))
  def sub(a: Int, b:Int): Rational = sub(new Rational(a, b))

  // OptimalizedRational immplementation (between classes):
  def optiAuto() = new OptimalizedRational(nom, denom)
  def isOpti = false

  // others:
  override def toString =  { opti(); nom + "/" + denom }
}

class OptimalizedRational(x: Int, y: Int) extends Rational(x, y) {

    // constructor:
    val d = gcd(x, y)
    override def nom = x / d
    override def denom = y / d

    // basic behaviour via method overriding:
    override def add(a: Int, b: Int) = super.add(a, b).opti().optiAuto()
    override def sub(a: Int, b: Int) = super.sub(a, b).opti().optiAuto()

    // OptimalizedRational immplementation (between classes):
    def optiNoAuto() = new Rational(nom, denom)
  override def isOpti = true
}

// TESTING:

new Rational(2, 3).optiAuto().add(1, 2).sub(3, 4)
new Rational(10, 24).opti().toString()
new OptimalizedRational(10, 24).toString()
 new Rational(10, 24).toString()
}

1 Ответ

0 голосов
/ 28 июня 2018

Каждый Rational объект является неизменным. Почему бы просто не добавить логику, которая просто оптимизирует Rational при создании экземпляра?

class Rational(x: Int, y: Int) {

  protected def gcd(a: Int, b: Int): Int = {
        if (b == 0) a
        else gcd(b, a % b)
  }

  //Calculate once
  val d = gcd(x, y)
  val xOverD = x / d
  val yOverD = y / d

  def nom = xOverD
  def denom = yOverD

  def add(that: Rational): Rational =
    new Rational((nom*that.denom + that.nom*denom), (denom*that.denom))

  def neg: Rational =
    new Rational(-nom, denom)

  def sub(that: Rational): Rational =
    add(that.neg)

  override def toString =  { nom + "/" + denom }
}

В качестве альтернативы, если вы действительно хотите разделить их, будет достаточно просто переопределить nom и denom методы , вам все равно придется переопределить методы, чтобы иметь возвращаемый тип OptimalizedRational, чтобы сделать это немного приятнее, мы можем использовать неявное преобразование для преобразования всех наших Rational s в OptimalizedRationals

class OptimalizedRational(x: Int, y: Int) extends Rational(x, y) {

  //Calculate once
  private val d = gcd(x, y)
  private val xOverD = x / d
  private val yOverD = y / d

  override def nom = xOverD
  override def denom = yOverD

  private implicit def convert(rational: Rational): OptimalizedRational =
    new OptimalizedRational(rational.nom, rational.denom)

  override def add(that: Rational): OptimalizedRational =
    super.add(that)

  override def neg: OptimalizedRational =
    super.neg

  override def sub(that: Rational): OptimalizedRational =
    super.add(super.neg)
}
...