これは、OCamlの2次Béツィアー関数の合理的な実装ですか?
質問
友人がコードベースで二次Béツィアー曲線関数に出くわし、スイッチテーブルの巨大なラットネストを使用して計算を実行しました。彼は、巨大なコードブロックを置き換えることができる単一の短い表現を見つけるように私に挑戦しました。
2つの異なる好奇心を満たすために、OCamlで関数を実装しようと思いました。私は非常に初心者のOCamlプログラマであり、この関数にも精通していないため、この特定の実装はGoogleを介して実現するのは困難です。
関数のパフォーマンス/正確さと実装の両方についての批評は大歓迎です。
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.);;
解決
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)
他のヒント
2つの提案があります:
b2i
が戻った後に List.rev
を呼び出す必要があります。これにより、ocamlは末尾再帰の最適化を活用できます。 ocamlが現在の実装をどれだけうまく処理できるかはわかりませんが、 List.rev
は末尾再帰です。 この投稿では次のように行われていることがわかります。それ。
また、反復の解像度を?(epsilon = 0.1)
のようなオプションの引数にすることもできます。
ocamlプログラマーとして、P1とP2が実際に定数である限り、それ以外はここでそれほど間違っているとは思いません。それをコンパイルして、List.revを末尾再帰の内外に移動する場合のアセンブリの違いを確認します。
これはpicayuneの場合がありますが、 hd
はリストパラメーターの適切な名前ではありません。 List.hd
は、リストの最初の要素を返す標準関数です。リストの名前として hd
を使用すると、混乱が生じます。