0

我在 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 个三角形

...

更新:

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

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

更新 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

4 回答 4

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 个空格而不是制表符。在您可以摆脱它的地方使用简洁的名称,它使代码更容易扫描结构。分号是不必要的噪音。

于 2009-11-15T08:42:09.240 回答
1

流封装了潜在无界序列的惰性计算。它们可能很难使用——或者至少令人惊讶——但确实符合您的要求。检查 Scaladoc,查找博客并搜索 Scala 邮件列表档案,以了解在没有充分理解它们对懒惰意味着什么以及 2.7 库中的实现缺陷的情况下,围绕它们的使用的许多悲惨故事。 ..

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

兰德尔·舒尔茨

于 2009-11-14T17:11:43.183 回答
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它清楚地表明的那样,这是完全没有功能的。如果您想以迭代方式但功能性地执行此操作,则需要将“状态”作为参数传递给类似于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()
于 2009-11-14T18:01:01.760 回答
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;
}
于 2009-11-16T04:51:07.547 回答