Внедряя номера - PullRequest
       13

Внедряя номера

7 голосов
/ 12 февраля 2012

Я довольно новичок в Скале. Я хочу написать несколько математических объектов (Complex, Polynomial и т. Д.), Которые закрыты для некоторых операций (+, -, *) таким образом, чтобы их можно было использовать в обобщениях и неявных приведениях.

Кажется, я решил первый бит.

trait GroupUnderAddition[T] {
  def + (t : T) : T
}

case class Real(d : Double) extends GroupUnderAddition[Real] {
  def + (r : Real) = Real(d + r.d)
}

case class Complex(re : Double, im : Double) extends GroupUnderAddition[Complex] {
  def + (c : Complex) = Complex(re + c.re, im + c.im)
}

object Test {
  implicit def real_to_complex(r : Real) = Complex(r.d, 0)

  def test[G <: GroupUnderAddition[G]](a : G, b : G) = a + b

  def main(args : Array[String]) {
    println(test(Real(5), Real(2)))
  }
}

Теперь, как мне написать test (), чтобы

test(Real(5), Complex(2, 1))

возвращает Комплекс (7, 1)?

Ответы [ 3 ]

2 голосов
/ 12 февраля 2012

Основная идея заключается в том, что все GroupUnderAddition не совместимы, поэтому, поскольку вы, похоже, хотите работать со сложной алгеброй, я бы предложил создать суперкласс, включающий GoupUnderAddition.Однако не рекомендуется делать это классом дел (смотрите предупреждения, если у вас есть case class расширение case class)

trait GroupUnderAddition[T] {
  def + (t : T) : T
}

class ComplexAlgebra(_re:Double, _im:Double) extends(GroupUnderAddition[ComplexAlgebra]) {
  val re = _re
  val im = _im     
  def + (c : ComplexAlgebra) = new ComplexAlgebra(re + c.re, im + c.im)  
}

case class Real(d : Double) extends ComplexAlgebra(d, 0)

case class Complex(real : Double, imaginary : Double) extends ComplexAlgebra(real,imaginary)

object Test {

  def test(a : ComplexAlgebra, b : ComplexAlgebra) = a + b

  def main(args : Array[String]) {
    println(test(Real(5), Real(2)))
  }
}
1 голос
/ 13 февраля 2012

Настоящая проблема здесь заключается в том, что вы делаете (неверное) предположение, что test(Real(5), Complex(2, 1)) четко определен любым образом, учитывая то, что вы написали. Учтите следующее:

case class Word(s : String) extends GroupUnderAddition[Word] {
  def +(w : Word) = Word(s + w.s)
}

Это полностью удовлетворяет вашему определению «GroupUnderAddition», но нет смысла пытаться добавить Word («Hello») в Real (2). Каков результат?

То, что вы пытаетесь закодировать, - это конкретный оператор сложения в большей области - он кажется областью многочленов над C - и указание, что некоторые его подгруппы закрыты под оператором сложения. Подход ChrisJamesC можно с радостью распространить на кольцо многочленов, которое будет захватывать то, что вы хотели.

1 голос
/ 13 февраля 2012

Проблема в том, что implicit def не рассматривается для преобразования аргументов, если вы не укажете определение метода для этого.

Следовательно, если у вас есть что-то вроде Real(5).foo и foo определено только для сложного, implicit def будет работать для этого.

Если у вас есть такой метод, как: def foo(c : Complex) = ..., вы не можете вызывать его с помощью foo(Real(5)).

Если вы хотите применить неявное преобразование, вы должны указать свой метод так, чтобы его аргументы могли быть преобразованы. Для описанного выше метода foo вы можете сделать это так:

def foo[T](c : T)(implicit ct : T => Complex) = ...`

Тогда допустимо позвонить foo(Real(5)), и будет использовано преобразование.

Адаптированный к вашей конкретной проблеме, вы можете написать метод тестирования следующим образом:

def test[G <: GroupUnderAddition[G],T](a : T, b : G)(implicit ag: T => G) = a + b

Указывая, что неявные преобразования из T в G должны приниматься во внимание, вы разрешаете методу test принимать теперь test(Real(5), Complex(2,1)).

Однако, это не будет работать наоборот. Так что вы пока не можете назвать его с test(Complex(2,1), Real(5)), потому что нет неявного преобразования второго аргумента.

Простой способ учесть оба преобразования - написать так:

def test[G <: GroupUnderAddition[G],T1, T2](a : T1, b : T2)(implicit ag: T1 => G, bg: T2 => G) = a + b

К сожалению, компилятор каким-то образом получает Any для G при вызове этого метода, как описано выше. Я не знаю сейчас, как решить эту проблему, и я опубликовал этот ответ в надежде, что кто-то еще может заполнить этот последний кусок головоломки.

Учитывая приведенное выше окончательное определение, вы можете по крайней мере вызывать метод любым способом, указав полные типы:

println(test[Complex,Real,Complex](Real(5), Complex(2, 1)))
println(test[Complex,Complex,Real](Complex(2,1), Real(5)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...