Является ли это разумной реализацией квадратичной функции Безье в OCaml?

Друг наткнулся на функцию квадратичной кривой Безье в своей кодовой базе, которая использовала гигантское крысиное гнездо таблицы переключения для выполнения вычислений. Он предложил мне найти одно короткое выражение, которое позволило бы ему заменить гигантский блок кода.

Пытаясь удовлетворить два разных любопытства, я решил попробовать реализовать эту функцию в 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.);;

person mbac32768    schedule 28.09.2008    source источник


Ответы (3)


b2 не является рекурсивным, поэтому [let rec b2 n =] не нужен. Поскольку n никогда не меняется, нет необходимости использовать его в качестве аргумента для b2i, просто используйте n из объемлющей области. Ваша внутренняя функция должна зависеть от p0, p1 и p2, но я вижу, что она зависит от -10., n**2 и 10. Функция также имеет вид карты из [ 0.0; 1,0; 2,0; ...; п.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)
person Thelema    schedule 28.09.2008

У меня есть два предложения:

Вы должны вызывать List.rev после возврата b2i, чтобы ocaml мог использовать оптимизацию хвостовой рекурсии. Я не уверен, насколько хорошо ocaml справится с текущей реализацией, хотя List.rev является хвостовой рекурсией. Вы заметите, что в этом сообщении это делается так тот.

Кроме того, вы можете сделать разрешение итерации необязательным аргументом, например ?(epsilon=0.1).

Как программист ocaml, я не вижу здесь ничего плохого, кроме того, что P1 и P2 на самом деле являются константами. Скомпилируйте его и посмотрите, какая разница в сборке между перемещением List.rev внутри или вне хвостовой рекурсии.

person nlucaroni    schedule 28.09.2008
comment
кодовый путь list.rev не повлияет на хвостовую рекурсию b2i. Хорошая идея на эпсилон. Вероятно, p1 и p2 тоже должны стать аргументами. - person Thelema; 28.09.2008

Это может быть пустяком, но hd не является хорошим именем для параметра списка. List.hd — это стандартная функция, которая возвращает первый элемент списка. Использование hd в качестве имени для списка приведет к путанице.

person Chris Conway    schedule 28.09.2008
comment
какой идентификатор вы используете для такого аргумента/списка? - person Thelema; 28.09.2008
comment
Я использую такие имена, как xs, ys и zs для списков, и x, y, z для элементов этих списков. Не сказать, что это лучший способ. - person Chris Conway; 28.09.2008
comment
так как это аккумулятор, я люблю использовать акк - person nlucaroni; 28.09.2008