How can duplication be eliminated from the following Haskell snippet? - haskell

The following function clearly has duplication between the two list comprehensions, but how can it be eliminated without increasing the total length of the code? I've got a sneaky feeling there's a nice abstraction lurking here but I just can't see it...
letterAt :: [Word] -> Int -> Int -> Maybe Char
letterAt wrds x y = listToMaybe $
[wtext !! (x - wx) |
Word wx wy wtext Across <- wrds,
wy == y && x >= wx && x < wx + length wtext] ++
[wtext !! (y - wy) |
Word wx wy wtext Down <- wrds,
wx == x && y >= wy && y < wy + length wtext]
Some background:
The function is taken from a crossword program. The crossword is represented as [Word], where
data Word = Word { startX :: Int,
startY :: Int,
text :: String,
sense :: Sense }
data Sense = Across | Down
The words where sense == Across start at position (startX, startY) and continue in the positive x direction, and those where sense == Down continue in the positive y direction. The aim of the function is to get the character at location (x, y) in a Just, or Nothing if there isn't a character there.
Feel free to point out any other sins against Haskell I've committed here, I've just started with the language and still trying to get to grips with it!

Here are some points about your code:
It is better to use filter when want to select certain elements of a list based on the predicate.
Since you just want the first element satisfying certain predicate you can use Data.List.find
Your conditions looks symmetric so you can define a transform function as
transform f x y (Word wx wy wtext Across) = f wtext wy wx y x
transform f x y (Word wx wy wtext Down) = f wtext wx wy x y
Now writing the code will require writing conditions only once
letterAt :: [Word] -> Int -> Int -> Maybe Char
letterAt wrds x y = (transform charValue x y) <$> find (transform condition x y) wrds
where
condition wtext wx wy x y = wx == x && y >= wy && y < wy + length wtext
charValue wtext wx wy x y = wtext !! (y-wy)

Satvik, thanks for your answer which got me thinking along the right lines. I separated out the condition and transformation functions as you suggested, then realized that it would be simpler to transform the data rather than the functions and put everything back into a list comprehension for readability:
letterAt :: [Word] -> Int -> Int -> Maybe Char
letterAt wrds x y = listToMaybe
[wtext !! x' | Word wx wy wtext sens <- wrds,
let (x', y') = case sens of
Across -> (x - wx, y - wy)
Down -> (y - wy, x - wx),
y' == 0, x' >= 0, x' < length wtext ]
You pointed out that it's better to use Data.find in this case.. is this for readability or efficiency? I'm guessing that because lists are lazy in Haskell, the above code would stop after the first item in the list comprehension was evaluated, is this right?

Related

Counting the occurrences of chararacters in a string

I got the task to count the number of occurrences of each (lower case) character in a string. I am not allowed to use any function of the library, I came up with the following, working solution.
occur :: String -> [(Char,Int)]
occur y = [ (x,count x y) | x<-['a'..'z'], count x y > 0]
I was trying at first:
occur2 :: String -> [(Char,Int)]
occur2 y = [ (x,z) | x<-['a'..'z'], z<- count x y, count x y > 0]
I defined the helper function count like this:
count :: Char -> String -> Int
count k str = length [n | n <- str, n == k]
Two questions:
Why is occur2 not working?
Is there any way to define occur without my aux function count?
occur2 isn't working because count x y is not a list, so it can't be used for a generator expression like in z <- count x y. Instead, use a let expression.
You can remove the count definition by inlining it.
occur :: String -> [(Char,Int)]
occur y = [ (x,z) | x <- ['a'..'z'], let z = length [n | n <- y, n == x], z > 0]
If you were to use libraries, a simple and efficient implementation would be to use a MultiSet.
import qualified Data.MultiSet as MS
occur :: String -> [(Char,Int)]
occur = MS.toAscOccurList . MS.fromList . filter (\c -> c >= 'a' && c <= 'z')

Project Euler #24 in Haskell

I am trying to solve the problems from Project Euler using Haskell, but I got sucked at #24
I'm trying to use factorials to solve problem but just can't work for the last three digits, here is my code:
import Data.List
fact n = product [n, n-1 .. 1]
recur :: Int -> Int -> [Int] -> [Int]
recur x y arr
| y > 1 = arr !! d : recur r (y-1) (delete (arr !! d) arr)
| otherwise = arr
where d = x `div` fact y
r = x `mod` fact y
main::IO()
main = print(recur 1000000 9 [0..9])
(I know it is now not really "functional")
I managed to get result [2,7,8,3,9,1,4,5,0,6], while the right answer I accidently figured out by hand is 2783915460.
I just want to know why this algorithm doesn't work for the last three digits. Thanks.
Unadulterated divMod is wrong for this algorithm. You need
dvm x facty | r == 0 = (d-1, facty)
| otherwise = (d, r)
where
(d, r) = divMod x facty
instead:
recur x y arr
.......
.......
where (d, r) = x `dvm` fact y
We cannot have zero combinations to do left. Zero means none.
Also the pattern guard condition should be changed to y > 0. Only when the length of the remaining choices list is 1 (at which point y is 0) there's no more choices to be made and we just use the last available digit left.

Just Int to Int

This code either returns the first factor of an Integer starting from 2 or returns nothing if it's a prime.
Example: firstFactorOf 24 returns "Just 2"
Example: firstFactorOf 11 returns "Nothing"
My question is, how would I return the value 2 rather than "Just 2" if there is a factor or return the value x if there is no factor.
firstFactorOf x
| m == Nothing = m
| otherwise = m
where m =(find p [2..x-1])
p y = mod x y == 0
//RETURNS:
ghci> firstFactorOf 24
Just 2
ghci> firstFactorOf 11
Nothing
Haskell is statically typed, meaning that you can define a function Maybe a -> a, but the question is what to do with the Nothing case.
Haskell has two functions that can be helpful here: fromMaybe and fromJust:
fromMaybe :: a -> Maybe a -> a
fromJust :: Maybe a -> a
fromJust simply assumes that you will always provide it a Just x, and return x, in the other case, it will throw an exception.
fromMaybe on the other hand expects two parameters, the first - an a is the "default case" the value that should be returned in case of Nothing. Next it is given a Maybe a and in case it is a Just x, x is returned. In the other case (Nothing) as said before the default is returned.
In your comment you say x should be returned in case no such factor exists. So I propose you define a new function:
firstFactorOfJust :: Integral a => a -> a
firstFactorOfJust x = fromMaybe x $ firstFactorOf x
So this function firstFactorOfJust calls your firstFactorOf function and if the result is Nothing, x will be returned. In the other case, the outcome of firstFactorOf will be returned (but only the Integral part, not the Just ... part).
EDIT (simplified)
Based on your own answer that had the intend to simplify things a bit, I had the idea that you can simplify it a bit more:
firstFactorOf x | Just z <- find ((0 ==) . mod x) [2..x-1] = z
| otherwise = x
and since we are all fan of optimization, you can already stop after sqrt(x) iterations (a well known optimization in prime checking):
isqrt :: Int -> Int
isqrt = floor . sqrt . fromIntegral
firstFactorOf x | Just z <- find ((0 ==) . mod x) [2..isqrt x] = z
| otherwise = x
Simplified question
For some reason there was some peculiarly complicated aspect in your question:
firstFactorOf x
| m == Nothing = m
| otherwise = m
where m =(find p [2..x-1])
p y = mod x y == 0
Why do you use guards to make a distinction between two cases that generate the exact same output? You can fold this into:
firstFactorOf x = m
where m = (find p [2..x-1])
p y = mod x y == 0
and even further:
firstFactorOf x = find p [2..x-1]
where p y = mod x y == 0
If you want it to return the first factor of x, or x, then this should work:
firstFactorOf x =
let
p y = mod x y == 0
m = (find p [2..x-1])
in
fromMaybe x m
import Data.List
import Data.Maybe
firstFactorOf x
| m == Nothing = x
| otherwise = fromJust m
where m =(find p [2..x-1])
p y = mod x y == 0
This was what I was after. Not sure why you guys made this so complicated.

Project Euler #4 using Haskell

I hope this works by just pasting and running it with "runghc euler4.hs 1000". Since I am having a hard time learning Haskell, can someone perhaps tell me how I could improve here? Especially all those "fromIntegral" are a mess.
module Main where
import System.Environment
main :: IO ()
main = do
args <- getArgs
let
hBound = read (args !! 0)::Int
squarePal = pal hBound
lBound = floor $ fromIntegral squarePal /
(fromIntegral hBound / fromIntegral squarePal)
euler = maximum $ takeWhile (>squarePal) [ x | y <- [lBound..hBound],
z <- [y..hBound],
let x = y * z,
let s = show x,
s == reverse s ]
putStrLn $ show euler
pal :: Int -> Int
pal n
| show pow == reverse (show pow) = n
| otherwise = pal (n-1)
where
pow = n^2
If what you want is integer division, you should use div instead of converting back and forth to Integral in order to use ordinary /.
module Main where
import System.Environment
main :: IO ()
main = do
(arg:_) <- getArgs
let
hBound = read arg :: Int
squarePal = pal hBound
lBound = squarePal * squarePal `div` hBound
euler = maximum $ takeWhile (>squarePal) [ x | y <- [lBound..hBound],
z <- [y..hBound],
let x = y * z,
let s = show x,
s == reverse s ]
print euler
pal :: Int -> Int
pal n
| show pow == reverse (show pow) = n
| otherwise = pal (n - 1)
where
pow = n * n
(I've re-written the lbound expression, that used two /, and fixed some styling issues highlighted by hlint.)
Okay, couple of things:
First, it might be better to pass in a lower bound and an upper bound for this question, it makes it a little bit more expandable.
If you're only going to use the first two (one in your previous case) arguments from the CL, we can handle this with pattern matching easily and avoid yucky statements like (args !! 0):
(arg0:arg1:_) <- getArgs
Let's convert these to Ints:
let [a, b] = map (\x -> read x :: Int) [arg0,arg1]
Now we can reference a and b, our upper and lower bounds.
Next, let's make a function that runs through all of the numbers between an upper and lower bound and gets a list of their products:
products a b = [x*y | x <- [a..b], y <- [x..b]]
We do not have to run over each number twice, so we start x at our current y to get all of the different products.
from here, we'll want to make a method that filters out non-palindromes in some data set:
palindromes xs = filter palindrome xs
where palindrome x = show x == reverse $ show x
finally, in our main function:
print . maximum . palindromes $ products a b
Here's the full code if you would like to review it:
import System.Environment
main = do
(arg0:arg1:_) <- getArgs
let [a, b] = map (\x -> read x :: Int) [arg0,arg1]
print . maximum . palindromes $ products a b
products a b = [x*y | x <- [a..b], y <- [x..b]]
palindromes = filter palindrome
where palindrome x = (show x) == (reverse $ show x)

How to recursively compare the digits in a number in Haskell

I am doing problem 112 on Project Euler and came up with the following to test the example case (I'll change the number in answer to 0.99 to get the real answer):
isIncre x | x == 99 = False
| otherwise = isIncre' x
where
isIncre' x = ???
isDecre x = isIncre (read $ reverse $ show x :: Int)
isBouncy x = (isIncre x == False) && (isDecre x == False)
bouncers x = length [n|n<-[1..x],isBouncy n]
nonBouncers x = length [n|n<-[1..x],(isBouncy n) == False]
answer = head [x|x<-[1..],((bouncers x) / (nonBouncers x)) == 0.5]
But what I don't know how to do is define a function isIncre' which tests to see if the digits in a number are greater than or equal to the one on their left. I know it needs to be done recursively but how?
On a side note, I know I can only use / on two floating point numbers but how can I make the output of bouncers to be floating point number instead of an integer?
Edit:
Thanks for the help, but it didn't like the = when I changed isIncre to:
isIncre x | x <= 99 = False
| otherwise = isIncre' (mshow x)
where
isIncre' (x:y:xs) = (x <= y) && (isIncre' (y:xs))
isIncre' _ = True
The number 0.99 cannot be represented exactly in base 2. Hence you may want to avoid the use of floating point numbers for this assignment. Instead, to see whether exactly 99% of the numbers <= x are bouncers, test whether
100 * (x - bouncers x) == x
This works because it is (mathematically) the same as (x - bouncers x) == x / 100, which is true if (x - bouncers x) (the number of non-bouncy numbers) is 1% of x. Observe that there is thus no need to define nonBouncers.
Also, another way to define bouncers is
bouncers x = length $ filter isBouncy [1..x]
However, you should reconsider your design. Currently you are recalculating the number of bouncy numbers up to x, for every x that you try. So a lot of work is being done over and over. What you may instead want to do, is generate a sequence of tuples (x, n), where n is the number of bouncy numbers <= x. Observe that if there are n bouncy numbers <= x, then there are either n or n + 1 bouncy number <= x + 1.
More specifically, to calculate (x + 1, n'), all you need is (x, n) and the output of isbouncy (x + 1).
If you have a string representation of an integer number, you could write the isIncre function like this (ord converts a character to an integer and string is just a list of chars):
isIncre (x:y:xs) = ord x <= ord y && isIncre (y:xs)
isIncre _ = True
It could be even nicer to write the isIncre function without ord, working on any ordered type, then combine it with "map ord" when you call it instead. The implementation would then be just:
isIncre (x:y:xs) = x <= y && isIncre (y:xs)
isIncre _ = True
That could be called like this, if x is an integer number
isIncre (map ord (show x))
I would use really nice functional version of isIncre if you have string representation of intetger.
isIncre :: (Ord a) => [a] -> Bool
isIncre list = and $ zipWith (<=) list (tail list)
If not, just compose it with show.
isIncreNum :: Integer -> Bool
isIncreNum = isIncre . show

Resources