[英]Verbose List Comprehension in Haskell
我正在使用Haskell查找具有特殊属性的1到10000整数列表。 我做以下
[ number | number <- [1..10000], (isSpecial number)]
但是,我时不时地想出一些特殊的属性
很难满足
需要很长时间才能得到验证
结果,它在一些头几个例子之后就挂在那里了。
我不知道如何才能使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)
但是要当心!
重要的是, matches
和nonMatches
类型不是 nonMatches
多态的(即,没有以Num a => ...
或其他约束开头的类型)。 在上面的示例中,我通过使isSpecial
单态实现这一点,即强制将matches
和nonMatches
设置为单态,但是如果isSpecial
是多态的,则应为matches
或nonMatches
提供类型签名,以防止出现此问题。
以这种方式进行操作将使整个nonMatches
matches
和matches
列表在内存中实现。 如果要分区的原始列表很长,这可能会很昂贵。 (但是,对于现代计算机而言,几十万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
元素的块,像对整个列表一样过滤每个块,然后将每个块转换为一对其head元素和已过滤的块:
chunked_result = [ (h, [n | n <- chunk, isSpecial n])
| chunk@(h:_) <- chunksOf n input]
通过concatMap snd
这样的结果列表将给出原始的非“冗长”选项。
简单地打印结果列表时,调整n
值将影响“报告”进度的频率,显示已尝试和已发现的数字,周围带有一些无关紧要的“噪音”。
使用second concat . unzip
在块结果列表上second concat . unzip
有点类似于Daniel Wagner的分区思想(带有警告) (*),但您的设置值为n
,而不仅仅是1
。
如果存在因特定问题而引起的算法减速,请应用增长顺序运行时间估算 分析 。
(*)为了使其兼容,我们需要在中间的某个位置粘贴一些seq
,例如
chunked_result = [ (last s `seq` last chunk, s)
| chunk <- chunksOf n input
let s = [n | n <- chunk, isSpecial n] ]
或者其他的东西。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.