代码之家  ›  专栏  ›  技术社区  ›  James Black

尝试使sierpinski三角生成器具有函数式编程风格

  •  0
  • James Black  · 技术社区  · 15 年前

    我在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. 画三个三角形,每边一个 中心三角形的
    2. 画9个三角形,每边一个 三个三角形中 上一次迭代。
    3. 画27个三角形

    更新:

    对不起,我忘了问号,所以很难看到这个问题。

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

    更新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 回复  |  直到 15 年前
        1
  •  2
  •   Apocalisp    15 年前

    让你的函数返回一对。左半部分包含当前迭代的三角形。右半部分包含一个函数,它返回一对包含下一次迭代所需三角形的函数和一个函数…

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

    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个空格代替制表符。使用简洁的名称,这样可以让代码更容易地扫描结构。分号是不必要的噪音。

        2
  •  1
  •   Randall Schulz    15 年前

    流封装了潜在的无边界序列的延迟计算。他们可能很难与“或令人惊讶的,至少是”合作,但符合你的要求。检查scaladoc,寻找博客,搜索scala邮件列表档案,寻找围绕其使用的许多不幸故事,而不充分了解懒惰意味着什么,以及2.7库中的实现缺陷……

    很抱歉这么含糊,但虽然我用过一次,但我觉得没有资格尝试更具体的…

    兰达尔舒尔茨

        3
  •  1
  •   Daniel C. Sobral    15 年前

    我认为下面的代码忠实地复制了您的代码。你创造了一个 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 公开,这样你就可以制作第一个。下面是一个用法示例:

    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()
    
        4
  •  0
  •   James Black    15 年前

    这是我得到的最后一个答案,但我从来没有想过课程功能,除了使徒的答案。

    我不知道是否应该使用一个特性来改进这个特性,但我认为这是迭代的最佳方法。

    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;
    }