如何解决将分形树函数从递归转换为迭代
我在sml中编写了以下程序,以将分形树生成到网页中(我对此感到非常自豪?)。我使用了递归函数,因为sml中没有for循环。
val width = 1000.0;
val height = 1000.0;
val length = 350.0;
val scale = 1.0 / 1.618;
val header = "<!DOCTYPE html><html><body><svg width='1000' height='1000' version='1.1' xmlns='http://www.w3.org/2000/svg'> \n";
val footer = "</svg></body></html>";
val outfile = TextIO.openOut "fractal-tree.html";
fun tree(x,y,length,angle) =
if length > 1.0 then (
let
val x2 = x + length * (Math.cos angle)
val y2 = y + length * (Math.sin angle)
val outString = "<line x1='" ^ Real.toString x ^ "'"
^ " y1='" ^ Real.toString y ^ "'"
^ " x2='" ^ Real.toString x2 ^ "'"
^ " y2='" ^ Real.toString y2 ^ "'"
^ " style='stroke:dodgerblue;stroke-width:2'/> \n"
in
TextIO.output(outfile,outString);
tree(x2,y2,length * scale,angle + Math.pi / 2.5);
tree(x2,angle - Math.pi / 2.5)
end
)else ();
TextIO.output(outfile,header);
tree(width / 2.0,height,3.0 * Math.pi / 2.0);
TextIO.output(outfile,footer);
TextIO.cloSEOut outfile;
这只是次要的,但是我想知道是否有一种方法可以在控制台中显示我的树。当我尝试时,我只会得到一个实数序列,然后程序停止。
解决方法
做得好!
我需要一些需要更改的代码才能将其转换为迭代函数。
我确定了您代码的三个部分:
- 生成SVG字符串的部分
- 与文件交互的部分
- 递归调用的部分
我将这些部分分开,以便您可以随意组合它们,而无需彼此依赖。例如,您可以组成分形效果而不必组成文件系统效果。
关于迭代与递归,由于您使用的是功能语言,因此我建议继续递归。但是,因为您的递归模式是树形的(每个调用可能会创建另外两个调用),所以递归可能会溢出堆内存。
因此,迭代解决方案和 tail-recursive 解决方案将实现相同的结果:由于您同时向左和向右递归,但不能同时递归,因此可以使用显式而不是隐式存储堆栈,您稍后也将以其他方式递归。
这里是重写;我将在下面评论:
datatype Line = Line of { x1 : real,y1 : real,x2 : real,y2 : real }
datatype Seed = Seed of { x : real,y : real,length : real,angle : real }
val scale = 1.0 / 1.618
val initialAngle = 3.0 * Math.pi / 2.0
fun makeLine (Seed { x = x1,y = y1,length = length,angle = angle }) =
let
val x2 = x1 + length * (Math.cos angle)
val y2 = y1 + length * (Math.sin angle)
in
Line { x1 = x1,y1 = y1,x2 = x2,y2 = y2 }
end
fun makeSplits (Line line) (Seed seed) =
let
val newx = #x2 line
val newy = #y2 line
val newlen = #length seed * scale
val left = Seed { x = newx,y = newy,length = newlen,angle = (#angle seed) + Math.pi / 2.5 }
val right = Seed { x = newx,angle = (#angle seed) - Math.pi / 2.5 }
in
[ left,right ]
end
fun isBelowThreshold (Seed { length = derp,... }) =
derp < 1.0
fun makeLines seeds =
let
fun go [] lines = lines
| go (seed :: stack) lines =
if isBelowThreshold seed
then go stack lines
else let
val line = makeLine seed
val splits = makeSplits line seed
in
go (splits @ stack) (line :: lines)
end
in
go seeds []
end
val toStr = Real.toString
fun drawLine (Line { x1 = x1,y2 = y2 }) =
String.concat
[ "<line x1='",toStr x1,"'"," y1='",toStr y1," x2='",toStr x2," y2='",toStr y2," style='stroke:dodgerblue;stroke-width:2'/>\n"
]
fun drawLines lines =
String.concat (List.map drawLine lines)
fun drawFractal width height length =
let
val header = String.concat
[ "<!DOCTYPE html><html><body>","<svg width='",toStr width," height='",toStr height," version='1.1' xmlns='http://www.w3.org/2000/svg'>\n"
]
val initialSeed = Seed {
x = width / 2.0,y = height,angle = initialAngle
}
val lines = makeLines [ initialSeed ]
val footer = "</svg></body></html>"
in
String.concat [ header,drawLines lines,footer ]
end
fun writeToFile path s =
let val fd = TextIO.openOut path
in TextIO.output (fd,s)
; TextIO.closeOut fd
end
因此,Line
和Seed
数据类型不是严格必需的:您还可以使用四元组或未用{{1}包装的{ ... }
记录},例如使用以下类型别名之一:
datatype
但是要点是(1)将递归的行和起点表示为与SVG或文件系统操作无关的抽象事物,以及(2)通过命名来区分一个4元组与另一个4元组。 >
将它们包装在type Line = { x1 : real,y2 : real }
type Seed = real * real * real * real
中而不是类型别名中的一件好事是,这些类型将在REPL和错误消息中显示为datatype
和Line
。这使得两者之间的区别
Seed
我敢肯定有一种更简单的方法,其中从val makeLines = fn : (real * real * real * real) list -> (real * real * real * real) list
val makeLines = fn : Seed list -> Line list
推断Seed
中的角度,但是由于我对分形的有限的机械理解,至少我对“ part”有两个名字。的结果”({{1)}和“递归的一部分”(Line
)。
对于递归,您的Line
函数被命名为Seed
,并且要实现尾递归,将使内部函数tree
只要具有{{1 }}(称为makeLines
)。每个go
会生成一个seeds
和一些新种子;哪些移动到seed :: stack
中,只是为了澄清seed
主要是进行递归。
line
(或您的makeSplits
)是否因所有输入而终止取决于某些几何推理。不是数学家,这不是那么令人安慰。但是至少,如果您犯了一个错误,此时程序将耗尽内存,并且不会用分形填充硬盘。 :-D
改善的想法:
- 我想看看这棵树的一个版本,它可以随机选择两个或三个分支。这将涉及调用
go
中的伪随机数生成器,并生成当前的两个分割或具有不同角度的三个分割。 - 您可以在中心开始进行分形,然后将其分为3、4、5或6方向进行分割以制作雪花。但是看来,按照目前的星座,其中一些星座需要较小的初始长度以避免重叠。
- 分支机构可以根据其父对象对其颜色进行轻微的突变。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。