2011-01-09 56 views
0

这是一个元胞自动机规则(输入Boolean == Left,Center,Right Cell)并输出布尔值。在Scala中表示这个更好的方法是什么?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 
      }    
     } 
    } 

回答

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辅助方法来处理翻译。我也改名ruleapply,让你的类可以直接用作功能:话又说回来

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,并直接使用val的output。我会离开,作为一个练习留给读者......

全部放在一起(无用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

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 
0

一个附加点,如果你的ruleIds是为了常量,那么你可以(也应该)将它们声明为vals而不是null-arg defs。就像你的defs一样,定义的vals可以在特质中是抽象的,在类中是具体的。

trait Rule { 
     val ruleId : Int 
} 

class Rule90 extends Rule { 
     val ruleId= 90  
} 
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有这个了。

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 newState_90 = newStates(90) 

很酷。但是,您也可以使用原始的Rule定义完成大部分操作。我只是想玩这个想法,因为我喜欢细胞自动机。

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 
} 

此外,对@马杜克的回答延长,因为你传递的部分功能,它不需要涵盖所有情况:

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 
}