First question ever here, and newbie in both Common LISP and Haskell, please be kind. I have a function in Common LISP - code below - which is intended to tell whether the area of a triangle is an integral number (integer?).
(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)))
This is supposed to use Heron's formula to calculate the area of the triangle, given the size of the three sides, and decide whether it is an integer comparing the ceiling and the floor. We are told that the area of an equilateral triangle is never an integer. Therefore, to test whether the function is working, I ran it with the arguments 333
. Here is what I got in return:
CL-USER> (area-int-p 333 333 333)
NIL
Perfect! It works. To test it even more, I ran it with the arguments 3333
. This is what I got in return:
CL-USER> (area-int-p 3333 3333 3333)
T
Something is wrong, this is not supposed to happen! So, I try the following, hopefully equivalent Haskell function to see what happens:
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
This is what I get:
*Main> areaIntP 3333 3333 3333
False
*Main> areaIntP 333 333 333
False
Looks perfect. Encouraged by this, I use the below functions in Haskell to sum the perimeters of of isosceles triangles with the third side differing just one unit from the other sides, an integral area, and perimeter below 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
means upper limit, altSinir
lower limit by the way.)
Running sonuc
with the arguments 2
and 333333333
however, my stack flows over. Runnning the equivalent functions in Common LISP the stack is OK, but area-int-p
function is not reliable, probably because of the boundaries of the number type the interpreter deduces.
After all this, my question is two-fold:
1) How do I get round the problem in the Common LISP function area-int-p
?
2) How do I prevent the stack overflow with the Haskell functions above, either within Emacs or with GHCi run from the terminal?
Note for those who figure out what I am trying to achieve here: please don't tell me to use Java BigDecimal
and BigInteger
.
Edit after very good replies: I asked two questions in one, and received perfectly satisfying, newbie friendly answers and a note on style from very helpful people. Thank you.