Эта функция принимает два целых числа и возвращает список всех целых чисел в диапазоне [a, b]

Это решение, которое я написал.

let rec range_rec l a b = 
  if (a=b) then l@[b]
  else range_rec (l@[a], a+1, b);;

let range a b = range_rec [] a b;;

Я получаю сообщение об ошибке «Ошибка: это выражение имеет тип int list * int * int, но ожидалось выражение типа int». Может кто-нибудь пролить свет на то, почему я получаю эту ошибку?

Спасибо.

1
Sunday Programmer 28 Авг 2011 в 10:43

3 ответа

Лучший ответ

Должно получиться так:

let rec range_rec l a b = 
  if a = b then l @ [b]
  else range_rec (l @ [a]) (a + 1) b;;

let range a b = range_rec [] a b;;

Что я сделал:

  1. Изменен loop на range_rec
  2. Изменил (l@[a], a+1, b) на (l @ [a]) (a + 1) b. Первый - это триплет, а второй - 3 аргумента каррированной функции.
  3. Обратите внимание, что if (a = b) then можно записать как if a = b then.

Наконец, функцию можно сделать более эффективной, используя :: вместо @, выполняя цикл "назад". Например вроде гаше показали.

7
Lasse Espeholt 28 Авг 2011 в 20:07

Операция l @ [elem] ужасна с точки зрения производительности: поскольку a @ b линейно по длине a, добавление элемента в конец списка линейно по длине списка, что делает все ваше определение range_rec квадратично относительно |b-a|. Можно изменить код так, чтобы вместо него можно было использовать операцию постоянного времени elem::l.

let rec range a b =
  if a > b then []
  else a :: range (a + 1) b

Вы можете сделать дополнительные оптимизации, например сделать его хвостовой рекурсией, но по крайней мере сложность этого решения правильная.

6
gasche 28 Авг 2011 в 08:51

Чтобы улучшить решение, уже предложенное гашем, его можно сделать хвостовой рекурсией.

let int_range a b =
  let rec int_range_rec l a b =
    if a > b then l
    else int_range_rec (b :: l) a (b - 1)
  in (int_range_rec [] a b);;
1
slongfield 1 Апр 2014 в 05:41