После размножения в течение двух дней, я, наконец, нашел решение, которое компилируется без предупреждения и передает мой технический тест. Ниже приведена компилятивная выдержка из моего кода, чтобы показать, что требуется. Однако следует отметить, что код является не оп, потому что я ушел из части на самом деле выполнять перестановки:
/**
* A generic mix-in for permutations.
* <p>
* The <code>+</code> operator (and the apply function) is defined as the
* concatenation of this permutation and another permutation.
* This operator is called the group operator because it forms an algebraic
* group on the set of all moves.
* Note that this group is not abelian, that is the group operator is not
* commutative.
* <p>
* The <code>*</code> operator is the concatenation of a move with itself for
* <code>n</code> times, where <code>n</code> is an integer.
* This operator is called the scalar operator because the following subset(!)
* of the axioms for an algebraic module apply to it:
* <ul>
* <li>the operation is associative,
* that is (a*x)*y = a*(x*y)
* for any move a and any integers x and y.
* <li>the operation is a group homomorphism from integers to moves,
* that is a*(x+y) = a*x + a*y
* for any move a and any integers x and y.
* <li>the operation has one as its neutral element,
* that is a*1 = m for any move a.
* </ul>
*
* @param <P> The target type which represents the permutation resulting from
* mixing in this trait.
* @see Move3Spec for details of the specification.
*/
trait Permutation[P <: Permutation[P]] { this: P =>
def identity: P
def *(that: Int): P
def +(that: P): P
def unary_- : P
final def -(that: P) = this + -that
final def unary_+ = this
def simplify = this
/** Succeeds iff `that` is another permutation with an equivalent sequence. */
/*final*/ override def equals(that: Any): Boolean // = code omitted
/** Is consistent with equals. */
/*final*/ override def hashCode: Int // = code omitted
// Lots of other stuff: The term string, the permutation sequence, the order etc.
}
object Permutation {
trait Identity[P <: Permutation[P]] extends Permutation[P] { this: P =>
final override def identity = this
// Lots of other stuff.
}
trait Product[P <: Permutation[P]] extends Permutation[P] { this: P =>
val perm: P
val scalar: Int
final override lazy val simplify = simplifyTop(perm.simplify * scalar)
// Lots of other stuff.
}
trait Sum[P <: Permutation[P]] extends Permutation[P] { this: P =>
val perm1, perm2: P
final override lazy val simplify = simplifyTop(perm1.simplify + perm2.simplify)
// Lots of other stuff.
}
trait Inverse[P <: Permutation[P]] extends Permutation[P] { this: P =>
val perm: P
final override lazy val simplify = simplifyTop(-perm.simplify)
// Lots of other stuff.
}
private def simplifyTop[P <: Permutation[P]](p: P): P = {
// This is the prelude required to make the extraction work.
type Pr = Product[_ <: P]
type Su = Sum[_ <: P]
type In = Inverse[_ <: P]
object Pr { def unapply(p: Pr) = Some(p.perm, p.scalar) }
object Su { def unapply(s: Su) = Some(s.perm1, s.perm2) }
object In { def unapply(i: In) = Some(i.perm) }
import Permutation.{simplifyTop => s}
// Finally, here comes the pattern matching and the transformation of the
// composed permutation term.
// See how expressive and concise the code is - this is where Scala really
// shines!
p match {
case Pr(Pr(a, x), y) => s(a*(x*y))
case Su(Pr(a, x), Pr(b, y)) if a == b => s(a*(x + y))
case Su(a, Su(b, c)) => s(s(a + b) + c)
case In(Pr(a, x)) => s(s(-a)*x)
case In(a) if a == a.identity => a.identity
// Lots of other rules
case _ => p
}
} ensuring (_ == p)
// Lots of other stuff
}
/** Here's a simple application of the mix-in. */
class Foo extends Permutation[Foo] {
import Foo._
def identity: Foo = Identity
def *(that: Int): Foo = new Product(this, that)
def +(that: Foo): Foo = new Sum(this, that)
lazy val unary_- : Foo = new Inverse(this)
// Lots of other stuff
}
object Foo {
private object Identity
extends Foo with Permutation.Identity[Foo]
private class Product(val perm: Foo, val scalar: Int)
extends Foo with Permutation.Product[Foo]
private class Sum(val perm1: Foo, val perm2: Foo)
extends Foo with Permutation.Sum[Foo]
private class Inverse(val perm: Foo)
extends Foo with Permutation.Inverse[Foo]
// Lots of other stuff
}
Как вы можете видеть, решение, чтобы определить типы и объекты экстрактор, которые являются локальными для simplifyTop метод.
Я также привел небольшой пример того, как применять такой микс к классу Foo. Как вы можете видеть, Foo - это нечто большее, чем фабрика для составных перестановок собственного типа. Это большая выгода, если у вас много таких классов, которые в противном случае не связаны.
< декламация >
Однако, я не могу удержаться, чтобы сказать, что система типа Scala является безумно сложным! Я опытный разработчик библиотеки Java и очень хорошо разбираюсь в Java Generics. Тем не менее мне потребовалось два дня, чтобы разобраться в шести строках кода с тремя определениями типов и объектов! Если бы это было не для образовательных целей, я бы бросил этот подход.
Прямо сейчас, я искушаюсь оракулом, что Scala НЕ будет следующей большой вещью в плане языков программирования из-за этой сложности. Если вы разработчик Java, который чувствует себя немного неудобно в отношении дженериков Java сейчас (а не меня), тогда вы бы ненавидели систему типов Scala, поскольку она добавляет инварианты, коварианты и контраварианты к понятию Java-дженериков, если не сказать больше.
Все в целом, система типа Scala, кажется, обращается к большему количеству ученых, чем разработчикам. С научной точки зрения, приятно рассуждать о типе безопасности программы. С точки зрения разработчиков, время для выяснения этих деталей тратится впустую, потому что оно не позволяет им отказаться от функциональных аспектов программы.
Ничего, я продолжу с помощью Scala. Комбинация соответствия шаблонов, микширования и функций высокого порядка просто слишком сильная, чтобы пропустить. Тем не менее, я считаю, что Scala будет гораздо более продуктивным языком, если не будет слишком сложной системы типов.
</декламация >
Я хотел бы добавить, что код, показанный здесь отрывок в результате рефакторинга одного из исходных классов, где подстановка черта должна наноситься. Исходный код полностью функциональный, включая упрощение выражений. Если я упрощаю процедуру no-op, также работает восстановленный код. –
Есть ли вероятность, что вы сможете реорганизовать больше по этим строкам: http://www.artima.com/pins1ed/case-classes-and-pattern-matching.html? – huynhjl
Вот откуда я пришел с моей начальной версией. Тем не менее, мне нужно сделать это признаком смешения, а не классом case, потому что я хочу применить его к нескольким классам, которые связаны только удаленно. Это должно быть возможно с помощью экстрактора, но я не могу его скомпилировать. –