哈斯克尔的详细列表理解



我正在使用Haskell查找从1到10000的具有特殊属性的整数列表。我做以下事情

[ number | number <- [1..10000], (isSpecial number)]

然而,我时不时地想出一些特殊的属性,这些属性是

  1. 难以满足

  2. 需要很长时间才能验证

结果,在前几个例子之后,它就挂在那里了。

我想知道我怎样才能在Haskell中详细理解列表,所以我对Haskell的进步有一个很好的更新。

这或多或少是Robin Zigmond的意思:

checkNumbers :: IO [Int]
checkNumbers = filterM check [1..10000]
where
check number = do
print $ "Checking number" <> show number
pure $ isSpecial number

这将在检查每个数字之前打印"检查数字 x"。随意尝试check函数中的任何其他效果(或者,用你的话说,"冗长"(。

这是一种不需要IO的方法,而是依靠懒惰和程序员猜测条件的哪一面"发生的频率更高。只是为了玩一些东西,这里有一个稍微慢的函数,可以检查一个数字是否是 10 的倍数。此功能的细节并不重要,如果有任何不合理之处,请随时跳过它。我还将打开计时报告;你稍后会明白为什么。

> isSpecial :: Int -> Bool; isSpecial n = last [1..10000000] `seq` (n `mod` 10 == 0)
> :set +s

(每五年增加一次0

现在的想法是这样的:我们将使用partition将列表拆分为两个块,而不是你的列表理解,与谓词匹配的元素和不匹配的元素。我们将打印具有更多元素的其中一个,以便我们可以密切关注进度;当它完全打印出来时,另一个将被全面评估,我们可以随心所欲地检查它。

> :m + Data.List
> (matches, nonMatches) = partition isSpecial [1..20]
(0.00 secs, 0 bytes)
> nonMatches
[1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19]
(12.40 secs, 14,400,099,848 bytes)

显然,我无法在 StackOverflow 上描述这一点,但是当我做上述事情时,nonMatches列表中的数字慢慢地一一出现在我的终端上,很好地表明了它当前在列表中的位置。现在,当您打印matches时,完整列表或多或少是即时可用的,正如您可以从计时报告中看到的那样(即不再等待 12 秒(:

> matches
[10,20]
(0.01 secs, 64,112 bytes)

但要小心!

  1. 重要的是,matchesnonMatches具有不是类型类多态的类型(即没有以Num a => ...或其他约束开头的类型(。在上面的例子中,我通过使isSpecial单态来实现这一点,这迫使matchesnonMatches也成为单态的,但是如果你的isSpecial是多态的,你应该为matchesnonMatches提供一个类型签名,以防止这个问题。

  2. 这样做将导致整个nonMatchesmatches列表在内存中实现。如果要分区的原始列表很长,这可能会很昂贵。(但对于现代计算机来说,几十万Int秒并不是特别长。

Debug.Trace

你可以看看Debug.Trace.它允许将消息打印到控制台。但是由于Haskell很懒惰,控制打印的时间并不容易。也不建议将其用于生产:

Prelude Debug.Trace> import Debug.Trace
Prelude Debug.Trace> [x | x <- [1..10], traceShow (x, odd x) $ odd x]
(1,True)
[1(2,False)
(3,True)
,3(4,False)
(5,True)
,5(6,False)
(7,True)
,7(8,False)
(9,True)
,9(10,False)
]

随着计算的进行,我们通常希望看到尝试和发现的数字。

我通常做的是将输入列表分解为n元素块,像过滤整个列表一样过滤每个块,并将每个块转换为一对它的头元素和过滤后的块:

chunked_result = [ (h, [n | n <- chunk, isSpecial n])
| chunk@(h:_) <- chunksOf n input]

将此类结果列表放在concatMap snd会给出原始的非"详细"选项。

调整n值将影响在简单地打印结果列表时"报告"进度的频率,显示尝试和发现的数字,周围有一些无关紧要的"噪音"。

在块结果列表中使用second concat . unzip有点类似于丹尼尔瓦格纳的分区想法(有警告(,(*(您的设置值为n,而不仅仅是1

如果特定问题固有算法减速,请应用增长顺序运行时估计分析。


(*(为了使其兼容,我们需要在中间的某个地方贴一些seq,例如

chunked_result = [ (last s `seq` last chunk, s)
| chunk <- chunksOf n input
let s = [n | n <- chunk, isSpecial n] ]

什么的。

相关内容

  • 没有找到相关文章

最新更新