Error in nested list comprehension in Haskell code - list

I am trying to write the following list comprehension in Haskell and it doesn't typecheck. I am new at this and can't really figure out why.
something :: Int -> [Int]
something n = [[ 2 * x + 1 | x <- xs]|xs <- [3..n],i <- [1..],j <-[1..] ,xs == i+j+2*i*j,i<=j,i>=1]
This is what I see:
Couldn't match expected type `Int' with actual type `[t0]'
In the expression: [2 * x + 1 | x <- xs]
NB: There could be a lot more wrong with this piece of code.
Here's what I am really trying to learn to do. From a list of all naturals from 3 to n(which is the Int input to the function), I want to extract just the subset of numbers which can be written as i+j+2*i*j where i, j are integers and the i<=j and i>=1. To this subset list , I want to apply the function 2*x+1 to each element x and output a final list.
Hope that makes sense.

First of all you have a nested list comprehension, so you're creating a list of lists, so the return type should be [[Int]] (list of lists of ints) and not [Int] (list of ints).
Second of all xs is a number (because you take it out of a list of numbers), but its name suggests that it's a list and when you do x <- xs you're actually treating it as if it was a list.
In response to your edit: I don't really see why you thought you needed nested list comprehensions for this. If we just remove the nesting from your code, we get something that is pretty close to working (I've also renamed xs to x because calling a number xs is just confusing - I've also removed the condition that i is at least 1 because that's already a given since you take i from the list [1..]):
[ 2 * x + 1 | x <- [3..n], i <- [1..],j <-[1..] ,x == i+j+2*i*j,i<=j]
Now this compiles, but it will loop forever. Why does it loop forever? Because you take i and j from infinite lists. This means it will start with x=3, i=1, j=1 and then try all values for j from 1 to infinity before it will try the next value of i (so in other words it will never try the next value of i).
So what we need to do is to give i and j upper bounds. An easy upper bound to pick is x (if i or j are greater than x (and neither is smaller than 1), i+j+2*i*j can't possibly be equal to x), so you get:
[ 2 * x + 1 | x <- [3..n], i <- [1..x],j <-[1..x], x == i+j+2*i*j, i<=j]
This works, but it can still be simplified a bit by somewhat: If we take j from the list [i..n] instead of [1..n], we guarantee that j is at least i and we don't need the condition i<=j anymore, so we can write:
[ 2 * x + 1 | x <- [3..n], i <- [1..x], j <-[i..x], x == i+j+2*i*j]
PS: Doing it this way (iterating over all x and then iterating over all possible values of i and j for each x) is a bit inefficient, so you might reconsider your approach. On the other hand if all you need is something that works, this is fine.

First, a function name must not be upper-case.
xs <- [3..n] means that xs is an Int, but x <- xs uses it as a list.
The rest of the comprehension looks a little bit strange, too. If you care to explain what exactly you want to do, we might be able to help a little bit more. :-)
[Edit]
You get an infinite list of your numbers by using [i+j+2*i*j| j <-[2..], i<-[1..(j-1)]], but it's not sorted. [x| x <-[3..(2*n*n)], j <-[2..n], i<-[1..(j-1)], x==i+j+2*i*j] gives a sorted list of all such numbers smaller than 2n².

Let's start with what you've got.
something n = [[ 2 * x + 1 | x <- xs]|xs <- [3..n],i <- [1..],j <-[1..] ,xs == i+j+2*i*j,i<=j,i>=1]
The basic problem here is that you don't need a nested list comprehension.
something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [1..], x == i+j+2*i*j, i<=j, i>=1]
This will compile. But there is, as you suspect, a lot more wrong with this piece of code.
Let's start with the conditions. Testing for i>=1 is superfluous, given that i <- [1..].
something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [1..], x == i+j+2*i*j, i<=j]
Similarly, we can get rid of the i<=j condition if we start j off at i instead of at 1.
something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [i..], x == i+j+2*i*j]
It should be clear that values of j greater than (n - i) `div` (1 + 2 * i) cannot possibly result in an x ≤ n.
something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [i .. (n - i) `div` (1 + 2 * i)], x == i+j+2*i*j]
Similarly, values of i of n `div` 3 or above cannot possibly result in an x ≤ n.
something n = [ 2 * x + 1 | x <- [3..n], i <- [1 .. (n `div` 3) - 1], j <- [i .. (n - i) `div` (1 + 2 * i)],
x == i+j+2*i*j]
At this point we have done enough for something to actually generate results. But there are duplicate values (e.g. when (i,j) is either (1,7) or (2,4) you get x = 22), which I assume you don't want.
We filter them out using nub from Data.List.
something n = nub [ 2 * x + 1 | x <- [3..n], i <- [1 .. (n `div` 3) - 1],
j <- [i .. (n - i) `div` (1 + 2 * i)], x == i+j+2*i*j]
There's no need to check that x satisfies a condition when we could construct x to satisfy that condition in the first place. (You will want to satisfy yourself that 3 ≤ x ≤ n still.) This is more efficient.
something n = nub [ 2 * x + 1 | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
let x = i+j+2*i*j]
The results are no longer coming out in ascending order, so let's make sure they do.
something n = sort $ nub [ 2 * x + 1 | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
let x = i+j+2*i*j]
On a style point, the doubling and adding one is a separate calculation from ensuring that x can be expressed as i+j+2*i*j, so let's split them up.
something n = sort $ map f $ nub [ x | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
let x = i+j+2*i*j]
where f x = 2 * x + 1
This allows us to get rid of x from the list comprehension.
something n = sort $ map f $ nub [ i+j+2*i*j | i <- [1 .. (n `div` 3) - 1],
j <- [1 .. (n - i) `div` (1 + 2 * i)]]
where f x = 2 * x + 1
Done.

Related

Going through all items in list

Result of this function:
list = [x*x | x <- [0..100]]
res = id
. filter (\(x,y,sum) -> sum > 9)
. takeWhile (\(x,y,sum) -> sum < 100)
. nub
$ [(x, y, (sqrt (x) + sqrt (y))) | x <- list, y <- list]
is [(0, 100, 10), (0, 121, 11 ... (0, 9801 ,99)].
How can I make it work with other items in x's list?
A takeWhile :: (a -> Bool) -> [a] -> [a] is a function that - like the name says - takes elements while the condtions holds. From the moment one such element fails, then the rest of the list is not emitted anymore, regardless whether it yields valueable data or not.
Since your list is however finite, and you are interested in all configurations, you do not have to use takeWhile here, you can use filter, so:
res = id
. filter (\(x,y,sum) -> sum > 9)
. filter (\(x,y,sum) -> sum < 100)
. nub
$ [(x, y, (sqrt (x) + sqrt (y))) | x <- list, y <- list]
Now we have fixed this. But the code still has some redundant and inelegant aspects.
We better avoid calculating the square root, by modifying list to emit squares together with the original value. Furthermore we can simply make it an infinite list, which is usally better if we want to reuse it in other parts of the code:
squares = [ (x, x*x) | x <- [0..]]
Next we thus will take values for as long as the value square root is less than 100:
where sq100 = takeWhile (\(r,_) -> r < 100) squares
Now we can use list comprehension that takes square roots and squares of the two lists and calculate the sum of the square roots, such that the sum is greater than 9 and less than 100. We can thus write:
res = [ (x,y,sqq) | (sqx,x) <- sq100, (sqy,y) <- sq100, x <= y,
let sqq = sqx+sqy, sqq > 9, sqq < 100 ]
where sq100 = takeWhile (\(r,_) -> r < 100) squares
Here we thus take an x and square root sqx of x, we take an y and square root sqy of y, then we constraint x <= y to remove duplicates, calculate the sum of the square roots sqq = sqx+sqy and finally we check whether that sum if greater than 9 and less than 100.
This produces 2520 results.
The takeWhile is the culprit because the sum will be > 100 at some point, and then the list gets cut off.

How to calculate the sine function in Haskell?

Here is my problem: I need a Haskell function that computes an approximation of the sine of some number, using the associated Taylor serie ...
In C++ I wrote this:
double msin(double number, int counter = 0, double sum = 0)
{
// sin(x) = x - (x'3 / 3!) + (x'5 / 5!) - (x'7 / 7!) + (x'9 / 9!)
if (counter <= 20)
{
if (counter % 2 == 0)
sum += mpow(number, counter * 2 + 1) / mfak(counter * 2 + 1) ;
else
sum -= mpow(number, counter * 2 + 1) / mfak(counter * 2 + 1) ;
counter++;
sum = msin(number, counter, sum);
return sum;
}
return (sum* 180.0 / _PI);
}
Now I am trying to do it in Haskell and I have no idea how... For now I was trying something like this (it doesn't really work, but it is work in progress ;) ):
This works:
mfak number = if number < 2
then 1
else number *( mfak (number -1 ))
mpow number potenca = if potenca == 0
then 0
else if potenca == 1
then 1
else (number * (mpow number (potenca-1)))
This doesn't work:
msin :: Double -> Int -> Double -> Double
msin number counter sum = if counter <= 20
then if counter `mod` 2==0
then let sum = sum + (msin 1 (let counter = counter+1 in counter) sum) in sum
else let sum = sum + (msin 1 (let counter = counter+1 in counter) sum) in sum
else sum* 180.0 / 3.14
Updated....doesn't compile :/ "Couldn't match expected type Double' with actual type Int'"
msin :: Double -> Int -> Double -> Double
msin number counter sum = if counter <= 20
then if counter `mod` 2==0
then let sum' = sum + ((mpow number (counter*2+1))/(mfak counter*2+1)) in msin number (counter+1) sum'
else let sum' = sum - ((mpow number (counter*2+1))/(mfak counter*2+1)) in msin number (counter+1) sum'
else sum* 180.0 / 3.14
As you can see, the biggest problem is how to add something to "sum", increase "counter" and go in recursion again with these new values...
P.S. I am new to Haskell so try to explain your solution as much as you can please. I was reading some tutorials and that, but I can't find how to save the result of some expression into a value and then continue with other code after it... It just returns my value each time I try to do that, and I don't want that....
So thanks in advance for any help!
I would rework the algorithm a bit. First we can define the list of factorial inverses:
factorialInv :: [Double]
factorialInv = scanl (/) 1 [1..] -- 1/0! , 1/1! , 1/2! , 1/3! , ...
Then, we follow with the sine coefficients:
sineCoefficients :: [Double]
sineCoefficients = 0 : 1 : 0 : -1 : sineCoefficients
Then, given x, we multiply both the above lists with the powers of x, pointwise:
powerSeries :: [Double] -- ^ Coefficients
-> Double -- ^ Point x on which to compute the series
-> [Double] -- ^ Series terms
powerSeries cs x = zipWith3 (\a b c -> a * b * c) cs powers factorialInv
where powers = iterate (*x) 1 -- 1 , x , x^2 , x^3 , ...
Finally, we take the first 20 terms and sum them up.
sine :: Double -> Double
sine = sum . take 20 . powerSeries sineCoefficients
-- i.e., sine x = sum (take 20 (powerSeries sineCoefficients x))
The problem is expressions like let stevec = stevec+1 in stevec. Haskell is not an imperative language. This does not add one to stevec. Instead it defines stevec to be a number that is one more than itself. No such number exists, thus you will get an infinite loop or, if you are lucky, a crash.
Instead of
stevec++;
vsota = msin(stevilo, stevec, vsota);
You should use something like
let stevec' = stevec + 1
in msin stevilo stevec' vsota
or just
msin stevilo (stevec + 1) vsota
(There's also something here that I don't understand. You are going to need mpow and mfak. Where are they?)
As you can see the biggest problem is how to add something to "vsota",
In a functional language you would use recursion here - the variable vstota is implemented as a function parameter which is passed from call to call as a list is processed.
For example, to sum a list of numbers, we would write something like:
sum xs = go 0 xs
where go total [] = total
go total (x:xs) = go (total+x) xs
In an imperative language total would be a variable which gets updated. Here is is a function parameter which gets passed to the next recursive call to go.
In your case, I would first write a function which generates the terms of the power series:
sinusTerms n x = ... -- the first n terms of x - (x'3 / 3!) + (x'5 / 5!) - (x'7 / 7!) ...
and then use the sum function above:
sinus n x = sum (sinusTerms n x)
You may also use recursive lists definitions to get [x, x^3, x^5 ...] and [1, 1/3!, 1/5! ...] infinite sequences. When they are done, the rest is to multiply their items each by other and take the sum.
sinus count x = sum (take count $ zipWith (*) ifactorials xpowers)
where xpowers = x : map ((x*x)*) xpowers
ifactorials = 1 : zipWith (/) ifactorials [i*(i+1) | i <- [2, 4 .. ]]
Also, it would be better to define xpowers = iterate ((x*x)*) x, as it seems to be much more readable.
I’ve tried to follow your conventions as much as I could. For mfak and mpow, you should avoid using if as it is clearer to write them using pattern matching :
mfak :: Int -> Int
mfak 0 = 1
mfak 1 = 1
mfak n = n * mfak (n - 1)
mpow :: Double -> Int -> Double
mpow _ 0 = 1
mpow x 1 = x
mpow x p = x * mpow x (p - 1)
Before calculating the sinus, we create a list of coefficients [(sign, power, factorial)] :
x - (x^3 / 3!) + (x^5 / 5!) - (x^7 / 7!) + (x^9 / 9!)
→ [(1,1,1), (-1,3,6), (1,5,120), (-1,7,5040), (1,9,362880)]
The list is created infinite by a list comprehension. First we zip the lists [1,-1,1,-1,1,-1...] and [1,3,5,7,9,11...]. This gives us the list [(1,1), (-1,3), (1,5), (-1,7)...]. From this list, we create the final list [(1,1,1), (-1,3,6), (1,5,120), (-1,7,5040)...]:
sinCoeff :: [(Double, Int, Double)]
sinCoeff = [ (fromIntegral s, i, fromIntegral $ mfak i)
| (s, i) <- zip (cycle [1, -1]) [1,3..]]
(cycle repeats a list indefinitely, [1,3..] creates an infinite list which starts at 1 with a step of 2)
Finally, the msin function is near the definition. It also uses a list comprehension to achieve its goeal (note that I kept the * 180 / pi though I’m not sure it should be there. Haskell knows pi).
msin :: Int -> Double -> Double
msin n x = 180 * sum [ s * mpow x p / f | (s, p, f) <- take n sinCoeff] / pi
(take n sinCoeff returns the first n elements of a list)
You may try the previous code with the following :
main = do
print $ take 10 sinCoeff
print $ msin 5 0.5
print $ msin 10 0.5
The expression is of the form x*P(x2).
For maximal efficiency, the polynomial in x2 must be evaluated using the Horner rule rather than computing the powers of x2 separately.
The coefficient serie with the factorial values can be expressed recursively in Haskell, just like is commonly done for the Fibonacci series. Using the ghci interpreter as our testbed, we have:
$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/ :? for help
λ>
λ>
λ> nextCoeffs d c = c : (nextCoeffs (d+1) ((-c)/(fromIntegral $ (2*d+2)*(2*d+3))))
λ>
λ> allCoeffs = nextCoeffs 0 1.0
λ>
where d is the depth inside the serie and c the current coefficient.
Sanity check: the coefficient at depth 3 must be the inverse of 7!
λ>
λ> 1.0 /(allCoeffs !! 3)
-5040.0
λ>
The Horner rule can be rendered in Haskell thru the foldr1 :: (a -> a -> a) -> [a] -> a library function.
As is customary in Haskell, I take the liberty to put the term count as the leftmost argument because it is the one most likely to be held constant. This is for currying (partial evaluation) purposes.
So we have:
λ> :{
|λ> msin count x = let { s = x*x ; cs = take count allCoeffs ;
|λ> stepFn c acc = acc*s + c ; }
|λ> in x * (foldr1 stepFn cs)
|λ> :}
Sanity checks, taking 20 terms:
λ>
λ> pi
3.141592653589793
λ>
λ> msin 20 (pi/6)
0.49999999999999994
λ>
λ> msin 20 (pi/2)
1.0
λ>
Side note 1: final multiplication by 180 / π is only of interest for inverse trigonometric functions.
Side note 2: in practice, to get a reasonably fast convergence, one should reduce the input variable x into the [-π,+π] interval using the periodicity of the sine function.

haskell infinite list of incrementing pairs

Create an infinite list pairs :: [(Integer, Integer)] containing pairs of the form (m,n),
where each of m and n is a member of [0 ..]. An additional requirement is that if (m,n)
is a legit member of the list, then (elem (m,n) pairs) should return True in finite time.
An implementation of pairs that violates this requirement is considered a non- solution.
****Fresh edit Thank you for the comments, Lets see if I can make some progress****
pairs :: [(Integer, Integer)]
pairs = [(m,n) | t <- [0..], m <- [0..], n <-[0..], m+n == t]
Something like this? I just don't know where it's going to return True in finite time.
I feel the way the question is worded elem doesn't have to be part of my answer. Just if you call (elem (m,n) pairs) it should return true. Sound right?
Ignoring the helper method, the list comprehension you have will list out all pairs but the order of elements is a problem. You'll have a infinitely many pairs like (0, m) which are followed by infinitely many pairs like (1, m). Of course elem will forever iterate all the (0, m) pairs never reaching (1, m) or (2, m) etc.
I'm not sure why you have the helper method -- with it, you are only building a list of pairs like [(0,0), (1,1), (2,2), ...] because you've filtered on m = n. Was that part of the requirements?
Like #hammar suggested, start with 0 = m + n and list out the pairs (m, n). Then list pairs (m, n) where 1 = m + n. Then your list will look like [(0,0), (0,1), (1,0), (0,2), (1,1), (2,0), ...].
The helper function ensures that pairs is a list of the form [ (0,0) , (1,1) , (2,2) ... ].
So elem ( m , n ) pairs can be implemented as:
elem (m , n) _ | m == n = True
| otherwise = False
This is a constant time implementation.
I first posted
Prelude> let pairs = [(m, n) | t <- [0..]
, let m = head $ take 1 $ drop t [0..]
, let n = head $ take 1 $ drop (t + 1) [0..]]
Which, I believed answered the three conditions set by the professor. But hammar pointed out that if I chose this list as an answer, that is, the list of pairs of the form (t, t+1), then I might as well choose the list
repeat [(0,0)]
Well, both of these do seem to answer the professor's question, considering there seems to be no mention of the list having to contain all combinations of [0..] and [0..].
That aside, hammer helped me see how you can list all combinations, facilitating the evaluation of elem in finite time by building the infinite list from finite lists. Here are two other finite lists - less succinct than Hammar's suggestion of the diagonals - that seem to build all combinations of [0..] and [0..]:
edges = concat [concat [[(m,n),(n,m)] | let m = t, n <- take m [0..]] ++ [(t,t)]
| t <- [0..]]
*Main> take 9 edges
[(0,0),(1,0),(0,1),(1,1),(2,0),(0,2),(2,1),(1,2),(2,2)]
which construct the edges (t, 0..t) (0..t, t), and
oddSpirals size = concat [spiral m size' | m <- n] where
size' = if size < 3 then 3 else if even size then size - 1 else size
n = map (\y -> (fst y * size' + div size' 2, snd y * size' + div size' 2))
[(x, t-x) | let size' = 5, t <- [0..], x <- [0..t]]
spiral seed size = spiral' (size - 1) "-" 1 [seed]
spiral' limit op count result
| count == limit =
let op' = if op == "-" then (-) else (+)
m = foldl (\a b -> a ++ [(op' (fst $ last a) b, snd $ last a)]) result (replicate count 1)
nextOp = if op == "-" then "+" else "-"
nextOp' = if op == "-" then (+) else (-)
n = foldl (\a b -> a ++ [(fst $ last a, nextOp' (snd $ last a) b)]) m (replicate count 1)
n' = foldl (\a b -> a ++ [(nextOp' (fst $ last a) b, snd $ last a)]) n (replicate count 1)
in n'
| otherwise =
let op' = if op == "-" then (-) else (+)
m = foldl (\a b -> a ++ [(op' (fst $ last a) b, snd $ last a)]) result (replicate count 1)
nextOp = if op == "-" then "+" else "-"
nextOp' = if op == "-" then (+) else (-)
n = foldl (\a b -> a ++ [(fst $ last a, nextOp' (snd $ last a) b)]) m (replicate count 1)
in spiral' limit nextOp (count + 1) n
*Main> take 9 $ oddSpirals 3
[(1,1),(0,1),(0,2),(1,2),(2,2),(2,1),(2,0),(1,0),(0,0)]
which build clockwise spirals of length 'size' squared, superimposed on hammar's diagonals algorithm.
I believe the solution to your task is:
pairs = [(x,y) | u <- [0..], x <- [0..u], y <-[0..u] , u == x+y]

Haskell: Lists and foldl

I have a series of numbers: 0, 1, 3, 6, 10, 15,...
Basically, you add 1, then you add 2, then add 3, etc.
I have to make a function where I return this series of numbers in a list up to a given number, n. I want to use foldl.
so, series 5 should return [0, 1, 3, 6, 10, 15]
Here is what I have so far:
eachElem n = foldl (+) 0 [0..n]
series n = [x | x <- [(eachElem 0), (eachElem 1)..(eachElem n)]]
Basically, I figured that each element in the list was a foldl operation, and so I made a separate helper function (eachElem) to accomplish this.
However, it is returning a list much larger than what I want.
Eg. series 3 => [0,1,2,3,4,5,6] when it should really return [0,1,3,6]
Any ideas why this is?
scanl is better suited to what you're doing.
Its type is scanl :: (a -> b -> a) -> a -> [b] -> [a] -- its type signature is the same as foldl's, but it returns a list of incremental values, instead of just the final result.
I'll leave the rest as an exercise for you, since this seems like homework. Good luck!
If you are so adamant of using foldl you can do something like
series n = reverse $ foldl f [0] [1..n]
where f xs#(x:_) y = x+y:xs
In ghci
> series 5
[0,1,3,6,10,15]
But problem with foldl is you can not create infinite series.
You can have infinite series like
series = 0:zipWith (+) series [1..]
Then you can do something like
> take (5+1) series
[0,1,3,6,10,15]
I have not tried but you might also use unfoldr or similar concept to build your list.
scanl is the best here, but if you have to use fold try this
testso :: Integral a => a -> [a]
testso n = reverse $ foldl (\acc x -> head acc + x:acc ) [0] [1,2..n]
gives output as testso 10 [0,1,3,6,10,15,21,28,36,45,55].
Your definition of series is wrong.
[(eachElem 0), (eachElem 1)..(eachElem n)] becomes [0, 1, eachElem n] which is actually every number up to eachElem n.
You actually want to do this:
series n = [eachElem x | x <- [0..n]]
the definition
series n = [ x | x <- [(eachElem 0)..(eachElem n)]]
is wrong!
For instance:
because of
eachElem 0 -> 0
eachElem 3 -> 6
series 3 evaluates to
series 3 -> [(eachElem 0)..(eachElem 3)] -> [0..6] -> [0,1,2,3,4,5,6]
You need something like that
series' n = [ eachElem x | x <- [0..n]]
tests:
> let series' n = [ eachElem x | x <- [0..n]]
> let series n = [ x | x <- [(eachElem 0)..(eachElem n)]]
> series' 3
> [0,1,3,6]
> series 3
> [0,1,2,3,4,5,6]
> eachElem 0
> 0
> eachElem 3
> 6
When you write [a,b..c], a is the first element, c is the last element and b is the step, it's the interval between every element in the list and if you omit it, it will be defaulted to 1.
So let's have a look at your code, you do:
[x | x <- [(eachElem 0), (eachElem 1)..(eachElem n)]]
In your list comprehension, x will first take the value (eachElem 0) = 0
Then the next element will be (eachElem 0) + (eachElem 1) = 1
Then the ith elent will be (eachElem 0) + i*(eachElem 1 - eachElem 0) as long as the value is <= (eachElem n)
Hence your result: [0,1..(eachElem n)] which produces [0,1,2,3... and clearly isn't what you expected.
As suggested by amindfv, you should have a look at scanl.
You can cheat :-)
series x = foldl (\xs n -> (n*(n+1) `div` 2):xs) [] [x,(x-1)..0]

How to count the number of 1's surrounding a given element in a 2D list with Haskell?

Suppose I have the following nested list:
list =
[[0, 1, 0],
[1, 9, 1],
[1, 1, 0]]
Assuming you are only given the x and y coordinate of 9. How do I use Haskell code to find out how many 1's surrounds the number 9?
Let me clarify a bit more, assume the number 9 is positioned at (0, 0).
What I am trying to do is this:
int sum = 0;
for(int i = -1; i <= 1; i++){
for(int j = -1; j <= 1; j++){
if(i == 0 || j == 0) continue;
sum += list[i][j];
}
}
The positions surrounding (0,0) are the following coordinates:
(-1, -1) (0, -1) (1, -1)
(-1, 0) (1, 0)
(-1, 1) (0, 1) (1, 1)
list = [[0,1,0],[1,9,1],[1,1,0]]
s x y = sum [list !! j !! i | i <- [x-1..x+1], j <- [y-1..y+1], i /= x || j /= y]
--s 1 1 --> 5
Note that I there is no error correction if the coordinates are at the edge. You could implement this by adding more conditions to the comprehension.
A list of lists isn't the most efficient data structure if things get bigger. You could consider vectors, or a Map (Int,Int) Int (especially if you have many zeros that could be left out).
[Edit]
Here is a slightly faster version:
s x y xss = let snip i zs = take 3 $ drop (i-1) zs
sqr = map (snip x) $ snip y xss
in sum (concat sqr) - sqr !! 1 !! 1
First we "snip out" the 3 x 3 square, then we do all calculations on it. Again, coordinates on the edges would lead to wrong results.
Edit: switched to summing surrounding 8 rather than surrounding 4
How often do you just want the surrounding count for just one entry? If you want it for all the entries, lists still perform fairly well, you just have to look at it holistically.
module Grid where
import Data.List (zipWith4)
-- given a grid A, generate grid B s.t.
-- B(x,y) = A(x-1,y-1) + A(x,y-1) + A(x+1,y-1)
-- + A(x-1,y) + A(x+1,y)
-- + A(x-1,y+1) + A(x,y+1) + A(x+1,y+1)
-- (where undefined indexes are assumed to be 0)
surrsum :: [[Int]] -> [[Int]]
surrsum rs = zipWith3 merge rs ([] : init rs') (tail rs' ++ [[]])
where -- calculate the 3 element sums on each row, so we can reuse them
rs' = flip map rs $ \xs -> zipWith3 add3 xs (0 : xs) (tail xs ++ [0])
add3 a b c = a+b+c
add4 a b c d = a+b+c+d
merge [] _ _ = []
-- add the left cell, right cell, and the 3-element sums above and below (zero-padded)
merge as bs cs = zipWith4 add4 (0 : init as) (tail as ++ [0]) (bs ++ repeat 0) (cs ++ repeat 0)
-- given a grid A, replace entries not equal to 1 with 0
onesOnly :: [[Int]] -> [[Int]]
onesOnly = map . map $ \e -> if e == 1 then 1 else 0
list :: [[Int]]
list = [[0, 1, 0]
,[1, 9, 1]
,[1, 1, 0]]
Now you can drop down to ghci to see it work:
*Grid Control.Monad> mapM_ (putStrLn . unwords . map show) list
0 1 0
1 9 1
1 1 0
*Grid Control.Monad> mapM_ (putStrLn . unwords . map show) $ onesOnly list
0 1 0
1 0 1
1 1 0
*Grid Control.Monad> mapM_ (putStrLn . unwords . map show) . surrsum $ onesOnly list
2 2 2
3 5 2
2 3 2