这是 OCaml 中二次 Bézier 函数的合理实现吗?

发布于 2024-07-06 03:05:26 字数 745 浏览 6 评论 0原文

一位朋友在他的代码库中发现了一个二次贝塞尔曲线函数,该函数使用开关表的巨大老鼠巢来执行计算。 他要求我找到一个简短的表达式来替换巨大的代码块。

为了满足两种不同的好奇心,我想尝试在 OCaml 中实现该函数。 我是一个非常新手的 OCaml 程序员,我也不熟悉这个函数,而且这个特定的实现很难通过 Google 获得。

非常感谢对该函数的性能/正确性及其实现的批评。

二次贝塞尔曲线的实现:

let rec b2 n =
  let p1 = -10. in
  let p2 = 10. in
  let q = n*.n in
  let rec b2i n i hd =
    if i > n then
      List.rev hd
    else
      let t = i /. n in
      b2i n (i+.1.) ((((1.-.t)**2.)*.p1+.(2.*.t*.(1.-.t)*.q)+.(t**2.)*.p2) :: hd)
  in 
  b2i n 0. []
;;

let floatprint lst = 
  List.iter (fun f -> Printf.printf "%f; " f) lst ;;

floatprint (b2 8.);;

A friend came across a quadratic Bézier curve function in his codebase that used a gigantic rats nest of a switch table to perform the computation. He challenged me to find a single, short expression that would allow him to replace the gigantic block of code.

In attempting to satisfy two different curiosities, I thought I'd try implementing the function in OCaml. I'm a very novice OCaml programmer and I'm also unfamiliar with the function and this specific implementation is hard to come by via Google.

Critiques on both the function's performance/correctness as well as its implementation are very much appreciated.

Implementation of Quadratic Bézier Curve:

let rec b2 n =
  let p1 = -10. in
  let p2 = 10. in
  let q = n*.n in
  let rec b2i n i hd =
    if i > n then
      List.rev hd
    else
      let t = i /. n in
      b2i n (i+.1.) ((((1.-.t)**2.)*.p1+.(2.*.t*.(1.-.t)*.q)+.(t**2.)*.p2) :: hd)
  in 
  b2i n 0. []
;;

let floatprint lst = 
  List.iter (fun f -> Printf.printf "%f; " f) lst ;;

floatprint (b2 8.);;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

星軌x 2024-07-13 03:05:26

b2 不是递归的,因此不需要 [let rec b2 n =]。 由于 n 永远不会改变,因此无需将其作为 b2i 的参数,只需使用封闭范围中的 n 即可。 你的内部函数应该依赖于 p0、p1 和 p2,但我认为它依赖于 -10.、n**2 和 10。该函数还具有来自 [ 0.0; 的映射的形式。 1.0; 2.0; ...; n.0] 到最终值。 你能写一下吗:

let b i = 
  let t = i /. n in
  let tminus = (1.-.t) in
  (tminus *. tminus *. p0) +. (2. *. t *. tminus *. p1) +. (t *. t * p2)
in
List.map b ([generate list 1.0; 2.0; ... n.0])

生成列表 1.0...n.0 的函数可以是:(对于小 n)

let rec count m n = if m > n then [] else m :: (count (m+.1.) n)

b2 isn't recursive, so no need for [let rec b2 n =]. Since n never changes, no need to have it as argument to b2i, just use n from the enclosing scope. Your inner function should depend on p0, p1 and p2, but I see it depending on -10., n**2 and 10. The function also has the form of a map from [ 0.0; 1.0; 2.0; ...; n.0] to the final values. Could you write it:

let b i = 
  let t = i /. n in
  let tminus = (1.-.t) in
  (tminus *. tminus *. p0) +. (2. *. t *. tminus *. p1) +. (t *. t * p2)
in
List.map b ([generate list 1.0; 2.0; ... n.0])

A function to generate the list 1.0...n.0 could be: (for small n)

let rec count m n = if m > n then [] else m :: (count (m+.1.) n)
醉梦枕江山 2024-07-13 03:05:26

我有两个建议:

您应该在 b2i 返回后调用 List.rev,以便 ocaml 可以利用它的尾递归优化。 我不确定 OCaml 处理当前实现的效果如何,尽管 List.rev 是尾递归的。 您会注意到这篇文章中的做法如下那。

另外,您可以将迭代的分辨率设置为可选参数,例如 ?(epsilon=0.1)

作为一名 ocaml 程序员,除此之外我没有看到太多错误——只要 P1 和 P2 实际上是常量。 编译它,看看将 List.rev 移入或移出尾递归在汇编中有何不同。

I have two suggestions:

You should call List.rev after b2i returns so ocaml can exploit it's tail-recursion optimizations. I am not sure how well OCaml will deal with the current implementation, List.rev is tail-recursive though. You'll notice that in this post it is done like that.

Also, you can make the resolution of the iteration be an optional argument like ?(epsilon=0.1).

As an ocaml programmer I don't see much wrong here aside from that --as long as P1 and P2 are in fact constants. Compile it down and see what the difference in assembly is between moving List.rev inside or out of the tail-recursion.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文