Reputation: 71
[Disclaimer] I am very new to Haskell (and any FPL for that matter), just started learning today by reading YAHT. So my code might look "funny". Any help to improve the coding style would be appreciated as well.
I was trying to write a function in Haskell that generates a list with series 1 to n for a given value of n, starting with +1 and toggling the sign first after 1 number, then 2, then 3 and so on.
e.g. series 16 should produce [1,-2,-3,4,5,6,-7,-8,-9,-10,11,12,13,14,15,-16] (1 positive, 2 negative, 3 positive, 4 negative, ...).
I found that the sign changes after each triangular number, which equals the sum of first few natural numbers.
So I wrote this code:
module Test
where
--It accepts n and k, prints numbers 1 to n, starting with +1 and toggling their sign after each triangular number
series 0 = []
series n =
if isTriangular n
then series (getPrevTri n (n-1)) ++ getSeries (odd (n + (getPrevTri n (n-1)))) ((getPrevTri n (n-1)) + 1) (n - (getPrevTri n (n-1)))
else series (getPrevTri n (n-1)) ++ getSeries (odd ((getNextTri n (n+1)) + (getPrevTri n (n-1)))) ((getPrevTri n (n-1)) + 1) (n - (getPrevTri n (n-1)))
--The sign is negative for those numbers which follow an odd triangular number AND the triangular number previous to it is even
--OR an even number AND the triangular number previous to it is odd.
getSeries sign start 0 = []
getSeries sign start n =
if sign == True
then [start] ++ getSeries True (start+1) (n-1)
else [-start] ++ getSeries False (start+1) (n-1)
--Checks whether n is a triangular number or not
isTriangular 0 = False
isTriangular n =
checkSum n 1
--Checks whether n is equal to sum of first few natural numbers, starting from k
checkSum n 0 = False
checkSum n k =
if n == (k * k + k)/ 2
then True
else if n > (k * k + k)/ 2
then checkSum n (k+1)
else False
--Gets the triangular number just smaller than n, descending from k
getPrevTri 0 k = 0
getPrevTri n k =
if k <= n
then if isTriangular k
then truncate k
else getPrevTri n (k-1)
else 0
--Gets the triangular number just greater than n, starting from k
getNextTri 0 k = 1
getNextTri n k =
if k >= n
then if isTriangular k
then truncate k
else getNextTri n (k+1)
else 0
I had to add a call to "truncate" in "getPrevTri" and "gerNextTri" since it started producing fractional numbers. But still I'm getting this error:
*Test> :load "Test.hs"
[1 of 1] Compiling Test ( Test.hs, interpreted )
Ok, modules loaded: Test.
*Test> series 16
<interactive>:1:0:
Ambiguous type variable `t' in the constraints:
`Integral t' arising from a use of `series' at <interactive>:1:0-8
`RealFrac t' arising from a use of `series' at <interactive>:1:0-8
Probable fix: add a type signature that fixes these type variable(s)
*Test>
Could someone explain what is the source of this error?
And what does surprise me is that when I tried to debug this code, I modified it to http://pastebin.ca/1932564 which produced the similar error.
And then to http://pastebin.ca/1932556 and it surprisingly caused no error.
(Please find the output at the end of the respective posts.)
What I infer from it is that a call to
isTriangular n
causes a type error in
odd n
How is it possible when Haskell is a "pure" FPL and in which functions do not have any side effects?
I used GHCi, version 6.12.3 for these codes, on a Windows 7 x64 machine.
Upvotes: 2
Views: 506
Reputation: 2317
Because FUZxxI already gave you an answer you were looking for I will show you how your problem can be solved easier and a lot shorter. Just for information.
How would you solve your problem in your head? First, you have to 'generate' sequence containing [1,2,2,3,3,3,4,4,4,4 ... ]
just to know where to change sign. That sequence, expressed in Haskell notation, would be -
numbers = concatMap (\x -> replicate x x) [1..]
Then you have to 'combine' each number of this sequence with corresponding number from sequence from 1 to n to give it proper sign - that would be -
series n = zipWith (\a b -> a*(-1)^(b `mod` 2 + 1)) [1..n] numbers
Well, and that's it. You have the solution. You can even express it as one-liner without using numbers
variable. But i consider it less readable.
Upvotes: 3
Reputation: 93082
There is no numerical type which implements both Integral
(forced by odd
) and RealFrac
(forced by (/)
). (These are typeclasses, if you don't know what I'm talking about, just wait until your tutorial shows about this)
You may replace either /
by div
or do an explicit cast via fromIntegral
or similar. You may also do something like x/2 == 1
instead of odd x
.
Edit: In your second pastebin file, you did the conversions via truncate
, which is also possible.
Haskell strength is it's strong typing system, allowing you to do less programming errors but also involves the problem of having strange problem at obvious places. I would generally suggest you to provide type-informations at least at toplevel functions. (like myFunc :: Int -> Integer
). This improves both readability and safety, as the compiler prompts you suddently if something went wrong. In ghci, you can easily find out about type informations via the :t
command:
ghci> :t odd
odd :: (Integral a) => a -> Bool
Please notice, that you have to wrap parantheses around infix functions when using this:
ghci> :t ($)
($) :: (a -> b) -> a -> b
Upvotes: 4