关于python:为什么这个Haskell代码这么慢?

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编译的情况下执行得最好。


检查x是否是dictWords的元素可能非常慢。我假设类似的python实现将dictWords存储在集合或排序向量中(在后一种情况下使用二进制搜索)?看起来你可能也想在这里做同样的事。

使用这个单词表和下面的代码,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]