1
\$\begingroup\$

I have been given a list xs and a function f, and I want to know stuff like how many x <- xs satisfy (f x) `elem` xs or which ones do, etc. However, the problem is that the list xs is available only as an external file.

import Data.Char
import System.IO.Strict
import qualified Data.Set as Set

vowel x = elem x "aeiou"

toPigLatin :: String -> String
toPigLatin word
    | vowel (head word) = word ++ "ay"
    | not $ null [x | x <- word, vowel x] = let (v, c) = span (not.vowel) word in c ++ v ++ "ay"
    | otherwise = word


wordList = lines <$>  System.IO.Strict.readFile "toyWordSet"
wordSet = Set.fromList <$> wordList

good :: String -> IO Bool
good word = (Set.member (toPigLatin word)) <$> wordSet

mapMBool :: (a -> IO b) -> [a] -> IO [b]
mapMBool f l = do
                case l of
                    [] -> return []
                    (x:xs) -> do
                            b1 <- f x
                            bs <- mapMBool f xs
                            return (b1:bs)  

Ultimately, I want to compute wordList >>= mapMBool good, which is a value of type IO [Bool] which have True at the positions where the desired property is satisfied.

Now, I have three main concerns:

  1. When I run my code with a toyWordSet, it runs fine but I ultimately intend to replace it with /usr/share/dict/words, on which my PC freezes. How do I make my program more efficient?
  2. Writing a function like mapMBool is not the most elegant idea ever. How can I do this more elegantly?
  3. For some reason, I get some strange exceptions, when I try to use the lazy readFile instead of the strict version. What is happening? Why must I use strictness?

Other general comments on the coding style or practice is also welcome.

\$\endgroup\$
1
  • \$\begingroup\$ mapMBool is just mapM. \$\endgroup\$
    – Gurkenglas
    Jan 8 '18 at 19:14
5
\$\begingroup\$

I think you've missed the forest for the trees. While the origin of your list might be IO, there's no need to carry values around inside the IO monad just because that's where you found them.

Write your functions as though the external world doesn't exist. E.g.—

-- Was `good`
pigExists :: String -> Set String -> Bool
pigExists word set = Set.member (toPigLatin word) set

Write your actions to set up the values you need to use your IO-ignorant functions. E.g.—

main :: IO ()
main = do
    allWords <- words <$> readFile "wherever" -- equivalent to `allWords <- wordList`
    let pigHits = map (\word -> pigExists word (Set.fromList allWords)) allWords
    print $ length $ filter id pigHits -- Or some other IO action that does what you need

When in doubt, parameterize. You tripped yourself up by defining wordSet directly on wordList instead of just accepting any [String] value you're passed.

\$\endgroup\$
5
  • \$\begingroup\$ This solves the problem of getting frozen, but for a file with 45k words, this still takes forever ( > 30 minutes) \$\endgroup\$ Jan 9 '18 at 13:14
  • \$\begingroup\$ Hm... that is wildly excessive. You're running from GHCi, aren't you? Try compiling it. Running my compiled version on a 45k word file takes my machine 0.097s. \$\endgroup\$
    – bisserlis
    Jan 9 '18 at 18:42
  • \$\begingroup\$ I did. It does not seem to help at all. Can I see your code? Here is mine: pastebin.com/qzeFri4H \$\endgroup\$ Jan 10 '18 at 2:15
  • 1
    \$\begingroup\$ Humm... there's something strange going on. Change your last line to mapM_ putStrLn pigHits. Now change it to putStrLn $ unlines pigHits. The former finishes immediately for me, the latter runs until my patience is exceeded (so, longer than 15 seconds). I have no idea what's happening! How exciting. Try posting it as a mystery to Stack Overflow. \$\endgroup\$
    – bisserlis
    Jan 10 '18 at 4:20
  • \$\begingroup\$ That works. Thanks. I also tried sequence_ $ map putStrLn pigHits. That works too \$\endgroup\$ Jan 10 '18 at 4:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.