Первый вопрос здесь, новичок в Common LISP и Haskell, пожалуйста, будьте добры. У меня есть функция в Common LISP — код ниже — которая предназначена для определения того, является ли площадь треугольника целым числом (целым?).
(defun area-int-p (a b c)
(let* ((s (/ (+ a b c) 2))
(area (sqrt (* s (- s a) (- s b) (- s c)))))
(if (equal (ceiling area) (floor area))
t
nil)))
Предполагается, что для вычисления площади треугольника с учетом размер трех сторон, и решить, является ли это целым числом, сравнивая потолок и пол. Нам говорят, что площадь равностороннего треугольника никогда не бывает целым числом. Поэтому для проверки работоспособности функции я запускал ее с аргументами 333
. Вот что я получил взамен:
CL-USER> (area-int-p 333 333 333)
NIL
Идеально! Оно работает. Чтобы проверить это еще больше, я запустил его с аргументами 3333
. Вот что я получил взамен:
CL-USER> (area-int-p 3333 3333 3333)
T
Что-то не так, такого быть не должно! Итак, я пробую следующую, надеюсь, эквивалентную функцию Haskell, чтобы посмотреть, что произойдет:
areaIntP :: (Integral a) => a -> a -> a -> Bool
areaIntP a b c =
let aa = fromIntegral a
bb = fromIntegral b
cc = fromIntegral c
perimeter = aa + bb + cc
s = perimeter/2
area = sqrt(s * (s - aa) * (s - bb) * (s - cc))
in if ceiling area == floor area
then True
else False
Вот что я получаю:
*Main> areaIntP 3333 3333 3333
False
*Main> areaIntP 333 333 333
False
Выглядит идеально. Воодушевленный этим, я использую приведенные ниже функции в Haskell для суммирования периметров равнобедренных треугольников с третьей стороной, отличающейся всего на одну единицу от других сторон, интегральной площади и периметра меньше 1 000 000 000.
toplamArtilar :: Integral a => a -> a -> a -> a
toplamArtilar altSinir ustSinir toplam =
if ustSinir == altSinir
then toplam
else if areaIntP ustSinir ustSinir (ustSinir + 1) == True
then toplamArtilar altSinir (ustSinir - 1) (toplam + (3 * ustSinir + 1))
else toplamArtilar altSinir (ustSinir - 1) toplam
toplamEksiler :: Integral a => a -> a -> a -> a
toplamEksiler altSinir ustSinir toplam =
if ustSinir == altSinir
then toplam
else if areaIntP ustSinir ustSinir (ustSinir - 1) == True
then toplamEksiler altSinir (ustSinir - 1) (toplam + (3 * ustSinir - 1))
else toplamEksiler altSinir (ustSinir - 1) toplam
sonuc altSinir ustSinir =
toplamEksiler altSinir ustSinir (toplamArtilar altSinir ustSinir 0)
(ustSinir
означает верхний предел, altSinir
нижний предел, кстати.) Однако запуск sonuc
с аргументами 2
и 333333333
приводит к переполнению моего стека. При выполнении эквивалентных функций в Common LISP со стеком все в порядке, но функция area-int-p
ненадежна, вероятно, из-за ограничений числового типа, которые интерпретатор выводит. После всего этого мой вопрос двоякий:
1) Как обойти проблему в функции Common LISP area-int-p
?
2) Как предотвратить переполнение стека с помощью функций Haskell, описанных выше, либо в Emacs, либо при запуске GHCi из терминала?
Примечание для тех, кто понимает, чего я пытаюсь добиться: пожалуйста, не говорите мне использовать Java BigDecimal
и BigInteger
.
Редактировать после очень хороших ответов: я задал два вопроса в одном и получил вполне удовлетворительные, дружелюбные к новичкам ответы и заметку о стиле от очень полезных людей. Спасибо.