为什么这个 Haskell 代码这么慢?
Why is this Haskell code so slow?
我是 Haskell 的新手,我尝试制作一个拼字游戏求解器。它接受您当前拥有的字母,找到它们的所有排列并过滤掉那些字典单词。代码非常简单:
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 代码:
from itertools import permutations
letters = raw_input("please enter your letters (without spaces): ")
d = open('words')
dictionary = [line.rstrip('\n') 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 代码相比之下是 "incredibly slow",但是由于 Haskell 是静态类型的,我想我只是认为它应该更快,而不是更慢比 Python 还差。
检查 x
是否是 dictWords
的元素可能会非常慢。我假设您的类似 python 实现将 dictWords
存储在集合或排序向量中(在后一种情况下使用二进制搜索)?看来你可能想在这里做同样的事情。
使用 this word list 和下面的代码,Python 版本运行大约 30 秒,Haskell 版本需要 1.5 分钟。所以 Haskell 比较慢(可能是因为它使用的是链表,所有条件都相同,迭代速度较慢),但与 Python 相比,我不会称它为 "incredibly slow"。在任一版本中切换使用一组可将时间减少到 1 秒以下。
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 代码:
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]
I'm kind of new to Haskell and tried making a scrabble solver.
您可以通过使用更好的算法来显着改进。
而不是测试输入字母的每个排列,如果你
首先对它们进行排序,您只能进行一次字典查找并获得
所有可能的单词(字谜)可能由
他们(全部使用)。
这是将字典创建为 Data.Map 的代码。
创建地图需要启动成本,但之后
第一次查询后续查找非常快。
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.5MB)的word文件的地图创建时间约为4-5秒。使用 ByteString 或 Text 而不是 String 可能会获得更好的性能。
可以尝试的一些不错的字母组合:
steer rat tuna lapse groan neat
注意:使用 GHC 7.10.2 我发现这段代码在 没有 使用 -O2 编译的情况下表现最好。
我是 Haskell 的新手,我尝试制作一个拼字游戏求解器。它接受您当前拥有的字母,找到它们的所有排列并过滤掉那些字典单词。代码非常简单:
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 代码:
from itertools import permutations
letters = raw_input("please enter your letters (without spaces): ")
d = open('words')
dictionary = [line.rstrip('\n') 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 代码相比之下是 "incredibly slow",但是由于 Haskell 是静态类型的,我想我只是认为它应该更快,而不是更慢比 Python 还差。
检查 x
是否是 dictWords
的元素可能会非常慢。我假设您的类似 python 实现将 dictWords
存储在集合或排序向量中(在后一种情况下使用二进制搜索)?看来你可能想在这里做同样的事情。
使用 this word list 和下面的代码,Python 版本运行大约 30 秒,Haskell 版本需要 1.5 分钟。所以 Haskell 比较慢(可能是因为它使用的是链表,所有条件都相同,迭代速度较慢),但与 Python 相比,我不会称它为 "incredibly slow"。在任一版本中切换使用一组可将时间减少到 1 秒以下。
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 代码:
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]
I'm kind of new to Haskell and tried making a scrabble solver.
您可以通过使用更好的算法来显着改进。
而不是测试输入字母的每个排列,如果你 首先对它们进行排序,您只能进行一次字典查找并获得 所有可能的单词(字谜)可能由 他们(全部使用)。
这是将字典创建为 Data.Map 的代码。 创建地图需要启动成本,但之后 第一次查询后续查找非常快。
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.5MB)的word文件的地图创建时间约为4-5秒。使用 ByteString 或 Text 而不是 String 可能会获得更好的性能。
可以尝试的一些不错的字母组合:
steer rat tuna lapse groan neat
注意:使用 GHC 7.10.2 我发现这段代码在 没有 使用 -O2 编译的情况下表现最好。