尝试以函数式编程风格制作谢尔宾斯基三角形生成器
我在 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);
}
我希望让它成为一个迭代器,而不是让它成为递归的,这样我就可以继续进行下一次迭代,并且它将打印下一个级别,所以我的程序最初创建外三角形,然后绘制第一个内三角形。通过使其成为迭代器,我可以等待按键来进行下一次迭代,也许每次都会改变颜色。
之后,它到达这个函数,在那里它将循环,所以在每次迭代中:
- 绘制 3 个三角形,每边一个 中心三角形的
- 画 9 个三角形,每边一个 三个三角形中的 上一次迭代。
- 画 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;
}
I have a function in Scala, and the same function in JavaScript, but I don't think it is in a functional style.
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);
}
My hope is to have this be an iterator, rather than having it be recursive, so I can keep getting the next iteration, and it would print the next level, so my program initially creates the outer triangle, then draws the first inner triangle. By making it an iterator I could wait for a key press to do the next iteration, perhaps changing colors each time.
After that, it gets to this function, where it will loop, so on each iteration:
- Draw 3 triangles, one on each side
of the central triangle - Draw 9 triangles, one on each side
of the three triangles from the
previous iteration. - Draw 27 triangles
...
UPDATE:
Sorry, I forgot a question mark, so it is hard to see the question.
Basically, I would like to change it from a recursive function to one that I can call on demand, and have it draw the next iteration. How can I do that?
Update 2:
I have a solution that works, but I don't know which solution is better, mine or the one that is also an answer in this question:
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是我得出的最终答案,但除了 Aposcalisp 的答案之外,我从未想到过柯里化函数。
我不知道是否应该使用 Trait 来改善这一点,但我认为这是迭代的最佳方法。
Here is the final answer I came to, but I would never had thought of curried functions except for the answer from Aposcalisp.
I don't know if I should use a Trait in order to make this better, but I think this is the best way to iterate through.
让你的函数返回一对。左半部分包含当前迭代的三角形。右半部分包含一个函数,该函数返回一对包含下一次迭代的三角形和一个函数...
编辑以下是您如何重写您的解决方案:
我还没有尝试过这个,但是非常有用为此,您将获得一系列迭代,每次迭代都是一个三角形列表。要绘制迭代,只需将其从流中弹出并对其调用
drawTriangle
即可。风格提示:避免
foreach
。使用 2 或 3 个空格代替制表符。在可以避免的地方使用简洁的名称,它使代码更容易扫描结构。分号是不必要的噪音。Have your function return a pair. The left half contains the triangles for the current iteration. The right half contains a function that returns a pair containing the triangles for the next iteration and a function...
EDIT Here's how you might rewrite your solution:
I've not tried this out, but something very much to this effect will give you a stream of iterations, each iteration being a list of triangles. To draw an iteration, just pop it off the stream and call your
drawTriangle
on it.Style tips: Avoid
foreach
. Use 2 or 3 spaces instead of tabs. Use terse names where you can get away with it, it makes code easier to scan for structure. The semicolon is unnecessary noise.流封装了潜在无限序列的惰性计算。它们可能很难使用,或者至少令人惊讶,但确实符合您的要求。检查 Scaladoc、查找博客并搜索 Scala 邮件列表档案,了解围绕其使用的许多不幸故事,这些故事缺乏对懒惰意味着什么的充分理解,以及 2.7 库中的实现缺陷。 ..
抱歉说得这么模糊,但是虽然我曾经使用过它们,但我觉得没有资格尝试更具体......
Randall Schulz
Streams encapsulate lazy computations of potentially unbounded sequences. They can be tricky to work with—or surprising, at least—but do fit your requirements. Check the Scaladoc, look for blogs and search the Scala mailing list archives for the many stories of woe that surround their use in the absence of sufficient understanding of what it means for them to be lazy, as well as implementation defficiencies in the 2.7 library...
Sorry to be so vague, but while I've used them once, I don't feel qualified to try to be more specific...
Randall Schulz
我认为下面的代码忠实地重现了您的代码。您创建一个
迭代器
,然后像其他迭代器一样循环。
用法示例:
现在,正如其中的
var
清楚地表明的那样,这完全不起作用。如果您想以迭代方式但功能性地执行此操作,则需要将“状态”作为参数传递给类似于next
正在执行的函数:在本例中,我创建了
newIteration
公开,这样你就可以制作第一个。这是一个用法示例:I think the code below faithfully reproduces your code. You create an
Iterator
,and then just loop like any other iterator.
Usage example:
Now, as the
var
in it clearly shows, this is completely non-functional. If you want to do it iteratively but functionally, you need to pass the "state" as arguments to a function similar to whatnext
is doing:In this case I made
newIteration
public so you can produce the first one. Here is an usage example: