Одно наблюдение ... При обычном использовании вы обнаружите, что метод sliding
является самым простым способом подачи данных в ваши автоматы.Это работает примерно так:
scala> val input = Seq(1,2,3,4,5,6,7,8,9)
input: Seq[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> input.sliding(3).toList
res4: List[Seq[Int]] =List(
List(1, 2, 3),
List(2, 3, 4),
...
List(6, 7, 8),
List(7, 8, 9))
Чтобы убедиться, что количество последовательностей, выводимых на sliding
, равно количеству элементов ввода, вам необходимо заполнить последовательность ввода с любой стороны:
scala> (0 +: input :+ 0).sliding(3).toList
res9: List[Seq[Int]] = List(
List(0, 1, 2),
List(1, 2, 3),
...
List(7, 8, 9),
List(8, 9, 0))
Тогда хватит теории, вернемся к коду!
Для вашего примера (и потому, что я понимаю кое-что из основной проблемы), я дополняю последовательность значениями false
здесь.
Поскольку sliding
будет выводить последовательности, а не кортежи, я создал вспомогательный метод seqYieldsTrue
для обработки перевода.Я также переименовал rule
в apply
, чтобы ваш класс можно было напрямую использовать как функцию:
trait Rule {
def ruleId: Int //abstract
def yieldsTrue(input: (Boolean,Boolean,Boolean)): Boolean //abstract
override def toString: String = "Rule:" + ruleId
private def seqYieldsTrue(input: Seq[Boolean]) = {
assert(input.size == 3)
input match {
case Seq(a,b,c) => yieldsTrue((a,b,c))
case _ => error("invalid input size")
}
}
def apply(input: Seq[Boolean]) =
(false +: input :+ false).sliding(3) map { seqYieldsTrue }
}
class Rule90 extends Rule {
val ruleId = 90
val falsehoods = Seq(
(true, true, true),
(true, false, true),
(false, true, false),
(false, false, false)
)
def yieldsTrue(input: (Boolean,Boolean,Boolean)) = !falsehoods.contains(input)
}
Опять же, я сказал, что понял основную проблему!Так что давайте просто покончим со всеми этими утомительными ручными определениями правил, и пусть компилятор сгенерирует для нас многое:)
Если вы не возражаете против некоторой путаницы ...
class WolframAutomata(val ruleId: Int) extends Rule {
def pow2(x: Int) = math.pow(2,x).toInt
def isBitSet(x: Int, bit: Int) = (x & pow2(bit)) > 0
// 8 possible input patterns corresponding to
// different combinations of 3 input bits
val inputs = (0 to 7) map { id =>
Tuple3(
isBitSet(id, 2),
isBitSet(id, 1),
isBitSet(id, 0)
) -> id
} toMap
//each of the 8 input rules corresponds to one bit in the ruleId
val outputs = inputs mapValues { isBitSet(ruleId, _) }
def yieldsTrue(input: (Boolean,Boolean,Boolean)) = outputs(input)
}
(правило для генерации автоматов из идентификаторов, взятых отсюда: http://www.wolframscience.com/nksonline/page-53#previous)
Работая так, вы также можете свернуть логику обратно в черту правила, так как нет особой необходимости в отдельномабстрактная черта, если когда-нибудь будет только один подкласс. Вы также можете безопасно покончить с yieldsTrue
в этом случае и просто работать непосредственно с output
val. Я оставлю это в качестве упражнения для читателя ...
Собираем все вместе (бесполезные выходные строки REPL удалены):
scala> val r90 = new WolframAutomata(90)
r90: WolframAutomata = Rule:90
scala> def destringify(s:String) = s map { case 'X' => true; case _ => false } toSeq
scala> val input = destringify(".......X.......")
scala> val output = Iterator.iterate(input)(r90.apply(_).toSeq) toSeq
scala> def stringify(xs: Seq[Boolean]) = xs map {case true => "X"; case _ => "."} mkString
//can you see what it is yet?
scala> val sierpinski = output.take(10).map(stringify).mkString("\n")
sierpinski: String =
.......X.......
......X.X......
.....X...X.....
....X.X.X.X....
...X.......X...
..X.X.....X.X..
.X...X...X...X.
X.X.X.X.X.X.X.X
...............
...............
Пожалуйста, прости все вызовы toSeq
, они в основном для принудительной оценки, так что вы можете увидеть некоторые реальныевывод на REPL, а не только non-empty iterator