Why is this Haskell code so slow?
我对哈斯凯尔有点陌生,尝试着做一个拼字游戏解决者。它接收你现在拥有的字母,找到它们的所有排列,过滤掉那些字典里的单词。代码非常简单:
1 2 3 4 5 6 7 8 | import Data.List main = do dict <- readFile"words" letters <- getLine let dictWords = words dict let perms = permutations letters print [x | x <- perms, x `elem` dictWords] |
但是,与我用Python实现的非常相似的实现相比,它的速度非常慢。我做错了什么根本原因吗?
*编辑:这是我的python代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from itertools import permutations letters = raw_input("please enter your letters (without spaces):") d = open('words') dictionary = [line.rstrip(' ') for line in d.readlines()] d.close() perms = ["".join(p) for p in permutations(letters)] validWords = [] for p in perms: if p in dictionary: validWords.append(p) for validWord in validWords: print validWord |
号
我没有精确地计算时间,但大体上感觉Python实现的速度是haskell实现的2倍。也许我不应该说haskell代码相比之下"非常慢",但是由于haskell是静态类型的,我想我只是认为它应该比python快得多,而且根本不慢。
I'm kind of new to Haskell and tried making a scrabble solver.
号
通过使用更好的算法,您可以大大改进事情。
而不是测试输入字母的每一个排列,如果你首先对它们进行排序,您只能进行一次字典查找并获取所有可能形成的单词(变位词)它们(全部使用)。
下面是将字典创建为data.map的代码。创建地图有一个启动成本,但之后第一个查询随后的查找速度非常快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import Data.List import qualified Data.Map.Strict as Map import Control.Monad import System.IO main = do contents <- readFile"words" let pairs = [ (sort w, [w]) | w <- words contents ] dict = foldl' (\m (k,v) -> Map.insertWith (++) k v m) Map.empty pairs -- dict = foldr (\(k,v) m -> Map.insertWith (++) k v m) Map.empty pairs forever $ do putStr"Enter letters:">> hFlush stdout letters <- getLine case Map.lookup (sort letters) dict of Nothing -> putStrLn"No words." Just ws -> putStrLn $"Words:" ++ show ws |
236K字(2.5 MB)的Word文件的地图创建时间大约为4-5秒。通过使用字节或文本而不是字符串,可以获得更好的性能。
一些好的字母组合可以尝试:
1 | steer rat tuna lapse groan neat |
。
注意:使用ghc 7.10.2,我发现此代码在不使用-o2编译的情况下执行得最好。
检查
使用这个单词表和下面的代码,python版本大约在30秒内运行,haskell版本需要1.5分钟。因此,haskell速度较慢(可能是因为它使用的是一个链接列表,所有东西都相同,迭代速度较慢),但与python相比,我不会称之为"不可思议的慢"。在任何一个版本中切换到使用集合都会将时间缩短到1秒以下。
1 2 3 4 5 | from itertools import permutations f = open('twl06.txt') words = f.read().split() print [''.join(p) for p in permutations('apricot') if ''.join(p) in words] |
下面是基于集合的haskell代码:
1 2 3 4 5 6 7 8 9 | import Data.Set import Data.List main = do dict <- readFile"twl06.txt" let letters ="apricot" let dictWords = Data.Set.fromList $ words dict let perms = permutations letters print [x | x <- perms, member x dictWords] |
号