1

ある友人が、コードベースで、スイッチ テーブルの巨大なラット ネストを使用して計算を実行する 2 次ベジエ曲線関数に出くわしました。彼は私に、巨大なコード ブロックを置き換えることができる短い式を 1 つ見つけるように要求しました。

2 つの異なる好奇心を満たすために、この関数を 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.);;
4

3 に答える 3

3

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)
于 2008-09-28T14:49:45.083 に答える
2

2 つの提案があります。

ocaml が末尾再帰の最適化を利用できるように、戻りList.rev後に呼び出す必要があります。b2iocaml が現在の実装をどれだけうまく処理できるかはわかりませんが、List.rev末尾再帰です。この投稿では、そのように行われていることに気付くでしょう。

また、反復の解決を のようなオプションの引数にすることもできます?(epsilon=0.1)

ocaml プログラマーとして、P1 と P2 が実際に定数である限り、それを除けば、ここで大きな間違いはないと思います。それをコンパイルして、 List.rev を末尾再帰の内側または外側に移動した場合のアセンブリの違いを確認してください。

于 2008-09-28T03:32:23.727 に答える
1

これは picayune かもしれませんがhd、リスト パラメーターとしては適切な名前ではありません。List.hdリストの最初の要素を返す標準関数です。hdリストの名前として使用すると、混乱を招きます。

于 2008-09-28T15:20:40.343 に答える