这是细胞自动机规则(输入布尔值==左,中,右单元格)和输出布尔值。在Scala中表示这个的更好方法是什么?
trait Rule {
def ruleId() : Int
def rule(inputState:(Boolean, Boolean, Boolean)) : Boolean
override def toString : String = "Rule:" + ruleId
}
class Rule90 extends Rule {
def ruleId() = 90
def rule(inputState:(Boolean, Boolean, Boolean)) : Boolean = {
// Verbose version, show all 8 states
inputState match {
case (true, true, true) => false
case (true, false, true) => false
case (false, true, false) => false
case (false, false, false) => false
case _ => true
}
}
}
答案 0 :(得分:5)
而不是
inputState match {
case (true, true, true) => false
case (true, false, true) => false
case (false, true, false) => false
case (false, false, false) => false
case _ => true
}
你可以说
inputState match {
case (true, _, true) | (false, _, false) => false
case _ => true
}
或者,简单但可能不那么清楚(取决于目的/背景)
def rule(inputState:(Boolean, Boolean, Boolean)) = inputState._1 != inputState._3
答案 1 :(得分:5)
其他答案集中在如何优化模式匹配以缩短模式匹配。但是,我认为Rule90
可能只是一个例子。也许您的问题不是如何优化规则90的模式匹配,但是如果有更合适的方法来使用Scala结构定义规则类型。这是我的看法。
首先,我不建议为不同的规则制作子类。您应该有一个Rule
类,所有具体规则都是它的实例。这是我对Rule
类的定义:
case class Rule(val id:Int, f:PartialFunction[(Boolean,Boolean,Boolean),Boolean])
extends (((Boolean,Boolean,Boolean))=>Boolean) {
def apply(state:(Boolean,Boolean,Boolean)) = f(state)
}
现在,Rule
类也是一个合适的Scala函数。您可以非常轻松地实例化它,如下所示:
val rule90 = Rule(90, {
case (true, true, true) => false
case (true, false, true) => false
case (false, true, false) => false
case (false, false, false) => false
case _ => true
})
这就是我选择f
成为PartialFunction
的原因,因此我们可以使用Scala语法来定义部分函数而不需要任何开销,就像这样。缺点是如果匹配不详尽,编译器不会抱怨。你需要下定决心,这对你来说更重要:代码简洁或详尽的错误检查。
如果是后者,您可以将f
更改为Function
,方法是将其类型声明为((Boolean,Boolean,Boolean))=>Boolean
。在这种情况下,您需要通过将f
作为闭包传递给构造函数来声明特定规则。
您可以非常轻松地应用Rule
功能:
val state = (true, false, true)
val newState = rule90(state) // should be false
此外,还有更多技巧可以做。假设您在列表中包含所有规则:
val ruleList = List(rule01, rule02, rule03 /* .... */)
然后,您可以例如从规则ID到规则实例进行映射,如下所示:
val rules = ruleList.map(r=>(r.id, r)).toMap
您可以访问并使用这样的规则:
val state = (true, false, true)
val newState = rules(90)(state)
或者,为了获得所有规则的所有下一个状态:
val state = (true, false, true)
val newStates = rules mapValues _(state)
并访问其中一个结果:
val newState_90 = newStates(90)
非常酷。但是,您也可以使用原始Rule
定义完成大部分操作。我只想稍微想一想这个想法,因为我喜欢细胞自动机。
答案 2 :(得分:5)
一个观察结果......在典型的使用中,您会发现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)
}
(从此处获取的ID生成自动机的规则:http://www.wolframscience.com/nksonline/page-53#previous)
像这样工作,您也可以将逻辑回滚到Rule特征中,因为如果只有一个子类,则几乎不需要单独的抽象特征。在这种情况下,您也可以安全地取消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
答案 3 :(得分:2)
您可以使用提取器在输入状态值上添加含义
object EqualLeftAndRight {
def unapply(inputState:(Boolean, Boolean, Boolean)) = inputState._1 == inputState._3
}
def rule(inputState:(Boolean, Boolean, Boolean)) : Boolean =
inputState match {
case EqualLeftAndRight() => true
case _ => false
}
另外,要扩展@Madoc的答案,因为你传递了部分功能,所以不需要涵盖所有情况:
case class Rule(val id:Int)(f:PartialFunction[(Boolean,Boolean,Boolean),Boolean]) extends (((Boolean,Boolean,Boolean))=>Boolean) {
def apply(state:(Boolean,Boolean,Boolean)) = if (f.isDefinedAt(state)) f(state) else false
}
val rule90 = Rule(90) {
case EqualLeftAndRight() => true
}
答案 4 :(得分:0)
还有一点,如果你的ruleIds是常量,那么你可以(并且应该)将它们声明为val而不是null-arg defs。与defs一样,定义的val可以是特征中的抽象,也可以是类中的具体。
trait Rule {
val ruleId : Int
}
class Rule90 extends Rule {
val ruleId= 90
}
答案 5 :(得分:0)
进一步简化......
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_21).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def rule(inputState:(Boolean, Boolean, Boolean)) = inputState._1 != inputState._3
rule: (inputState: (Boolean, Boolean, Boolean))Boolean
scala> rule((true, false, true))
res0: Boolean = false
scala> rule((true, false, false))
res1: Boolean = true
scala> rule((false, false, false))
res2: Boolean = false
scala> rule((false, true, false))
res3: Boolean = false
scala> rule((false, true, true))
res4: Boolean = true
哎呀,对不起,我看到@Debilski已经有这个了。