试图以函数式编程风格制作sierpinski三角形生成器

时间:2009-11-14 15:20:55

标签: scala functional-programming fractals

我在Scala中有一个函数,在JavaScript中有相同的函数,但我不认为它是函数式的。

def drawSurroundingTriangles(startx : Double, starty : Double, width : Double) {
    var newwidth = width/2;
    var newstartx = startx + newwidth / 2;
    var newstarty = starty - newwidth;
    drawTriangle(newstartx, newstarty, newwidth);
    drawTriangle(newstartx - newwidth, starty + newwidth, newwidth);
    drawTriangle(newstartx + newwidth, starty + newwidth, newwidth);
    if(newwidth < 6)
        return;
    drawSurroundingTriangles(newstartx, newstarty, newwidth);
    drawSurroundingTriangles(newstartx - newwidth, starty + newwidth, newwidth);
    drawSurroundingTriangles(newstartx + newwidth, starty + newwidth, newwidth);
}

我的希望是让它成为一个迭代器,而不是让它递归,所以我可以继续进行下一次迭代,它会打印下一个级别,所以我的程序最初会创建外部三角形,然后绘制第一个内三角。通过使它成为迭代器,我可以等待按键进行下一次迭代,也许每次都改变颜色。

之后,它会进入这个函数,它将循环,所以每次迭代:

  1. 绘制3个三角形,每边一个 中央三角形
  2. 绘制9个三角形,每边一个 来自的三个三角形 上一次迭代。
  3. 绘制27个三角形
  4. ...

    更新

    抱歉,我忘了一个问号,所以很难看到问题。

    基本上,我想将它从递归函数更改为我可以按需调用的函数,并让它绘制下一次迭代。 我该怎么做?

    更新2:

    我有一个有效的解决方案,但我不知道哪个解决方案更好,我的或者也是这个问题的答案:

    def drawSurroundingTriangles(indexlist : List[(Double, Double, Double)]) : List[(Double, Double, Double)] = {
        var mylist = ListBuffer[(Double, Double, Double)]()
        indexlist.foreach{ 
            case (startx, starty, width) => { mylist ++ drawSingleTriangle(startx, starty, width) } }
    
        mylist.toList;
    }
    
    def drawSingleTriangle(startx : Double, starty : Double, width : Double) : List[(Double, Double, Double)] = {
        val newwidth = width/2;
        val newstartx = startx + newwidth / 2;
        val newstarty = starty - newwidth;
        var list = List((newstartx, newstarty, newwidth),
                ((newstartx - newwidth, starty + newwidth, newwidth)),
                (newstartx + newwidth, starty + newwidth, newwidth));
        list.foreach{ case (nstartx, nstarty, nwidth) => drawTriangle(nstartx, nstarty, nwidth)}
        list;
    }
    

4 个答案:

答案 0 :(得分:2)

让你的功能返回一对。左半部分包含当前迭代的三角形。右半部分包含一个函数,该函数返回包含下一次迭代的三角形和函数的对...

编辑以下是您重写解决方案的方法:

type Triangle = (Double, Double, Double)

def fractal(width : Double): Stream[List[Triangle]] = {
  val w2 = width / 2
  def surrounding(t: Triangle) = match t case (startx, starty, width) => {
    val w = width/2
    val x = startx + w / 2
    val y = starty - w
    List((x, y, w),
         (x - w, starty + w, w),
         (x + w, starty + w, w))
  }
  def s(tris: List[Triangle]): Stream[List[Triangle]] =
    Stream.cons(tris, s(tris.flatMap(surrounding(_))))
  s(List((w2/2, w2, w2)))
}

我没有试过这个,但是这个效果非常重要,它会给你一个迭代流,每个迭代都是一个三角形列表。要绘制迭代,只需将其从流中弹出并在其上调用drawTriangle

样式提示:避免使用foreach。使用2或3个空格而不是制表符。使用简洁的名称,你可以使用它,它使代码更容易扫描结构。分号是不必要的噪音。

答案 1 :(得分:1)

Streams封装了潜在无界序列的延迟计算。它们可能很难处理或者令人惊讶,至少 - 但确实符合您的要求。检查Scaladoc,查找Scala并搜索Scala邮件列表档案,查看围绕其使用的许多故事,但缺乏对它们懒惰意味着什么的充分理解,以及2.7库中的实施效率低下。 ..

很抱歉这么模糊,但是虽然我曾经使用过一次,但我觉得没有资格尝试更具体......

兰德尔舒尔茨

答案 2 :(得分:1)

我认为下面的代码忠实地再现了您的代码。您创建了Iterator, 然后像任何其他迭代器一样循环。

case class Triangle(startx: Double, starty: Double, width: Double)

class drawSurroundingTrianglesIterator(original: Triangle) extends Iterator[Unit] {
  private case class Iteration(old: Triangle, `new`: Triangle)
  private var iteration = List(newIteration(original))

  def hasNext = ! iteration.isEmpty
  def next = {
    iteration = iteration flatMap variants map newIteration
    iteration map (_.old) foreach draw
    iteration = iteration filter (_.`new`.width > 5)
  }

  private def newIteration(triangle: Triangle) = {
    import triangle._
    Iteration(triangle, Triangle(startx + width / 4, starty - width / 2, width / 2))
  }

  private def variants(iteration: Iteration) = {
    import iteration._
    import `new`._
    List(Triangle(startx, starty, width),
         Triangle(startx - width, old.starty + width, width),
         Triangle(startx + width, old.starty + width, width))
  }

  private def draw(triangle: Triangle) = {
    import triangle._
    drawTriangle(startx, starty, width)
  }
}

用法示例:

scala> new drawSurroundingTrianglesIterator(Triangle(100, 100, 40))
res1: drawSurroundingTrianglesIterator = non-empty iterator

scala> res1 foreach (x => x)
Drawing 110,000000, 80,000000, 20,000000
Drawing 90,000000, 120,000000, 20,000000
Drawing 130,000000, 120,000000, 20,000000
Drawing 115,000000, 70,000000, 10,000000
Drawing 105,000000, 90,000000, 10,000000
Drawing 125,000000, 90,000000, 10,000000
Drawing 95,000000, 110,000000, 10,000000
Drawing 85,000000, 130,000000, 10,000000
Drawing 105,000000, 130,000000, 10,000000
Drawing 135,000000, 110,000000, 10,000000
Drawing 125,000000, 130,000000, 10,000000
Drawing 145,000000, 130,000000, 10,000000

现在,正如其中var清楚地显示的那样,这完全是无功能的。如果你想迭代但功能上做,你需要将“state”作为参数传递给类似于next正在做的函数:

case class Triangle(startx: Double, starty: Double, width: Double)
case class Iteration(old: Triangle, `new`: Triangle)

object TriangleIterator {
  def iterate(from: List[Iteration]) = {
    val iteration = from flatMap variants map newIteration
    iteration map (_.old) foreach draw
    iteration filter (_.`new`.width > 5)
  }

  def newIteration(triangle: Triangle) = {
    import triangle._
    Iteration(triangle, Triangle(startx + width / 4, starty - width / 2, width / 2))
  }

  private def variants(iteration: Iteration) = {
    import iteration._
    import `new`._
    List(Triangle(startx, starty, width),
         Triangle(startx - width, old.starty + width, width),
         Triangle(startx + width, old.starty + width, width))
  }

  private def draw(triangle: Triangle) = {
    import triangle._
    drawTriangle(startx, starty, width)
  }
}

在这种情况下,我将newIteration公开,以便您可以制作第一个。{1}}这是一个用法示例:

scala> List(TriangleIterator.newIteration(Triangle(100, 100, 50)))
res0: List[Iteration] = List(Iteration(Triangle(100.0,100.0,50.0),Triangle(112.5,75.0,25.0)))

scala> TriangleIterator.iterate(res0)
Drawing 112,500000, 75,000000, 25,000000
Drawing 87,500000, 125,000000, 25,000000
Drawing 137,500000, 125,000000, 25,000000
res1: List[Iteration] = List(Iteration(Triangle(112.5,75.0,25.0),Triangle(118.75,62.5,12.5)), Iteration(Triangle(87.5,12
5.0,25.0),Triangle(93.75,112.5,12.5)), Iteration(Triangle(137.5,125.0,25.0),Triangle(143.75,112.5,12.5)))

scala> TriangleIterator.iterate(res1)
Drawing 118,750000, 62,500000, 12,500000
Drawing 106,250000, 87,500000, 12,500000
Drawing 131,250000, 87,500000, 12,500000
Drawing 93,750000, 112,500000, 12,500000
Drawing 81,250000, 137,500000, 12,500000
Drawing 106,250000, 137,500000, 12,500000
Drawing 143,750000, 112,500000, 12,500000
Drawing 131,250000, 137,500000, 12,500000
Drawing 156,250000, 137,500000, 12,500000
res2: List[Iteration] = List(Iteration(Triangle(118.75,62.5,12.5),Triangle(121.875,56.25,6.25)), Iteration(Triangle(106.
25,87.5,12.5),Triangle(109.375,81.25,6.25)), Iteration(Triangle(131.25,87.5,12.5),Triangle(134.375,81.25,6.25)), Iterati
on(Triangle(93.75,112.5,12.5),Triangle(96.875,106.25,6.25)), Iteration(Triangle(81.25,137.5,12.5),Triangle(84.375,131.25
,6.25)), Iteration(Triangle(106.25,137.5,12.5),Triangle(109.375,131.25,6.25)), Iteration(Triangle(143.75,112.5,12.5),Tri
angle(146.875,106.25,6.25)), Iteration(Triangle(131.25,137.5,12.5),Triangle(134.375,131.25,6.25)), Iteration(Triangle(15
6.25,137.5,12.5),Triangle(159.375,131.25,6.25)))

scala> TriangleIterator.iterate(res2)
Drawing 121,875000, 56,250000, 6,250000
Drawing 115,625000, 68,750000, 6,250000
Drawing 128,125000, 68,750000, 6,250000
Drawing 109,375000, 81,250000, 6,250000
Drawing 103,125000, 93,750000, 6,250000
Drawing 115,625000, 93,750000, 6,250000
Drawing 134,375000, 81,250000, 6,250000
Drawing 128,125000, 93,750000, 6,250000
Drawing 140,625000, 93,750000, 6,250000
Drawing 96,875000, 106,250000, 6,250000
Drawing 90,625000, 118,750000, 6,250000
Drawing 103,125000, 118,750000, 6,250000
Drawing 84,375000, 131,250000, 6,250000
Drawing 78,125000, 143,750000, 6,250000
Drawing 90,625000, 143,750000, 6,250000
Drawing 109,375000, 131,250000, 6,250000
Drawing 103,125000, 143,750000, 6,250000
Drawing 115,625000, 143,750000, 6,250000
Drawing 146,875000, 106,250000, 6,250000
Drawing 140,625000, 118,750000, 6,250000
Drawing 153,125000, 118,750000, 6,250000
Drawing 134,375000, 131,250000, 6,250000
Drawing 128,125000, 143,750000, 6,250000
Drawing 140,625000, 143,750000, 6,250000
Drawing 159,375000, 131,250000, 6,250000
Drawing 153,125000, 143,750000, 6,250000
Drawing 165,625000, 143,750000, 6,250000
res3: List[Iteration] = List()

答案 3 :(得分:0)

以下是我的最终答案,但除了Aposcalisp的答案外,我从未想过过咖喱的功能。

我不知道是否应该使用Trait来改善它,但我认为这是迭代的最好方法。

def drawFractal(width : Double) {
    val mywidth = width / 2;
    val drawSurroundingTriangles = drawSurroundingTrianglesComplete((startx, starty, width) => {
            val newwidth = width/2;
            val newstartx = startx + newwidth / 2;
            val newstarty = starty - newwidth;
            var list = List((newstartx, newstarty, newwidth),
                    ((newstartx - newwidth, starty + newwidth, newwidth)),
                    (newstartx + newwidth, starty + newwidth, newwidth));
            list.foreach{ case (nstartx, nstarty, nwidth) => drawTriangle(nstartx, nstarty, nwidth)}
            list;
    })_

    var mylist = drawSurroundingTriangles(List((mywidth/2, mywidth, mywidth)));
    mylist.foreach{ case (startx, starty, width) => print ("[" + startx + "," + starty + "," + width + "]\n")}
    print("\n");
    mylist = drawSurroundingTriangles(mylist);
    mylist.foreach{ case (startx, starty, width) => print ("[" + startx + "," + starty + "," + width + "]\n")}
}
def drawSurroundingTrianglesComplete(myfunc : (Double, Double, Double) => List[(Double, Double, Double)])
(indexlist : List[(Double, Double, Double)]) : 
    List[(Double, Double, Double)] = {
    var mylist = ListBuffer[(Double, Double, Double)]()
    indexlist.foreach{ 
        case (startx, starty, width) => { mylist ++= myfunc(startx, starty, width) } }
    mylist.toList;
}