Часто для того чтобы начать использовать новый язык/технологию нужно найти/выбрать какую-то весьма простую, но хоть сколько-нибудь интересную задачку. Предлагаю, в качестве такой задачи, взять задачу символьного дифференцирования, которая является одной из задач символьных вычислений. Задача символьного дифференцирования состоит в том, чтобы преобразовать одно арифметическое выражение (исходное выражение) в другое арифметическое выражение (результат символьного дифференцирования), которое называется производной этого выражения.
Так как цель этой статьи помочь читателю в изучении языка Scala, то будем рассматривать эту задачу в существенном упрощении, и сосредоточимся на самой, так называемой, «инженерной» задаче.
Прежде чем приступить к решению задачи, введем те самые упрощения.
Во-первых, будем считать, что исходное выражение является или константой или переменной.
Во-вторых, добавим в понятие выражение сумму двух выражений в привычном ее понимании, — как сложение двух величин (в данном случае, как сложение двух выражений).
Определим формальные требования к нашей задаче:
- Символьный дифференциал от константы, есть константа равная 0
- Символьный дифференциал от переменной, есть константа равная 1, если имя переменной совпадает с «х», в противном случае результатом символьного дифференцирования будет константа равная 0
- Символьный дифференциал от суммы, есть сумма символьных дифференциалов
Всюду далее я буду предполагать, что читатель имеет представление об основных концепциях OOP и некое первоначальное представление о самом языке Scala.
Начнем решение с выполнения простой задачи, а затем, итеративно, будем расширять имеющийся функционал. В качестве «базисной» задачи возьмем реализацию первого формального требования:
Символьный дифференциал от константы, есть константа равная 0
Итак, теперь все подготовительные работы закончены и можно начинать решать поставленные задачи.
Я всегда веду разработку любого функционала с того, как я хотел бы пользоваться полученным результатом, т.е. сначала определяю то, как я буду пользоваться функционалом, а уже потом, поняв архитектуру решения поставленной передо мной задачи, приступаю к работе.
Итак, наконец-то немного кода:
package expression
object SymbolicDifferentiation {
def main(args: Array[String]) {
// в качестве исходного выражения возьмем произвольную константу, например единицу.
val expression = new Constant(1);
// печатаем исходное выражение
println("Origin: " + expression);
// ожидаем "(1)"
// печатаем результат символьного дифференцирования исходного выражения
println("Result: " + expression.diff());
// ожидаем "(0)"
}
}
Начнем с главного — с исходного выражения, — фундаментального элемента поставленной задачи. Нам необходимо лишь описать поведение, поэтому используем ключевое слово trait:
package expression.core
/* Исходное выражение*/
trait Expression {
/* возвращает символьный дифференциал исходного выражения */
def diff(): Expression
}
Определим теперь класс Constant (константа), как конкретную реализацию исходного выражения Expression:
package expression.core
/* Константа */
class Constant(value: Number) extends Expression {
// требуем чтобы было указанно значение константы, иначе выбрасываем исключение
require(value != null, throw new IllegalArgumentException)
// возвращает значение константы
def getValue(): Number = value
// возвращает результат символьного дифференцирования константы
override def diff(): Expression = new Constant(0)
override def toString(): String = "(" + getValue() + ")"
}
Отлично, мы выполнили первое формальное требование. Приступим к выполнению второго.
Символьный дифференциал от переменной, есть константа равная 1, если имя переменной совпадает с «х», в противном случае результатом символьного дифференцирования будет константа равная 0
И, снова, начнем с того, что определим то, как мы хотим пользоваться будущим функционалом:
package expression
object SymbolicDifferentiation {
def main(args: Array[String]) {
// в качестве исходного выражения возьмем произвольную переменную, например "x"
val expression = new Variable("x");
// печатаем исходное выражение
println("Origin: " + expression);
// ожидаем "(x)"
// печатаем результат символьного дифференцирования исходного выражения
println("Result: " + expression.diff());
// ожидаем "(1)"
}
}
При разработке «функционала», удовлетворяющего второму функциональному требованию, создадим еще одну конкретизацию исходного выражения, — Variable(переменная):
package expression.core
/* Переменная */
class Variable(name: String) extends Expression {
// требуем, чтобы у переменной было указанно имя
require((name != null) && !"^\s*$".matches(name), throw new IllegalArgumentException)
// возвращает имя переменной
def getName(): String = name
// возвращает результат символьного дифференцирования
def diff(): Expression = if ("x" == name) { new Constant(1) } else { new Constant(0) }
override def toString(): String = "(" + getName() + ")"
}
и, наконец, переходим к заключительному третьему функциональному требованию:
Символьный дифференциал от суммы, есть сумма символьных дифференциалов
Как обычно, начнем с описания того, как хотим пользоваться будущим функционалом
package expression
object SymbolicDifferentiation {
def main(args: Array[String]) {
// в качестве исходного выражения возьмем сумму ( x + 1 )
val e = new Add(new Variable("x"), new Constant(1));
// печатаем исходное выражение
println("Origin: " + expression);
// ожидаем "((x) + (1))"
// печатаем результат символьного дифференцирования исходного выражения
println("Result: " + expression.diff());
// ожидаем "((1) + (0))"
}
}
Перейдем к определению последнего объекта поставленной задачи, — Add (сумма):
/* Cумма двух выражений */
class Add(firstSummand: Expression, secondSummand: Expression) extends Expression {
// требуем, чтобы были указанны оба слагаемых, иначе выбрасываем исключение
require((firstSummand != null) && (secondSummand != null), throw new IllegalArgumentException)
// возвращает первое слагаемое
def getFirstSummand(): Expression = firstSummand
// возвращает второе слагаемое
def getSecondSummand(): Expression = secondSummand
// возвращает результат символьного дифференцирования, - сумму символьных дифференциалов
override def diff(): Expression = new Add(getFirstSummand().diff(), getSecondSummand().diff())
override def toString(): String = "(" + getFirstSummand() + " + " + getSecondSummand() + ")"
}
Вот и всё. На основе решения задачи символьного дифференцирования и, принятых упрощений, реализовали вот такую небольшую программку, а заодно немного разобрались в том, как «использовать» OOP в Scala, по-крайней мере на ранних этапах знакомства с этим замечательным языком. Всем спасибо за внимание! Всем добра!
Используемые ресурсы:
— Официальный сайт, посвященный языку Scala.
Автор: jxcoder