我刚开始学习 Haskell,在阅读“Learn You a Haskell”一书时,我遇到了一个问题。这两种做本质上相同的事情的方式有什么区别:
qtz 1 = [1]
qtz x
| even x = x : qtz (x `div` 2)
| odd x = x : qtz (x * 3 + 1)
-- this
length (filter (>15) (map length (map qtz [1..100])))
-- and this
length (filter (\ xs -> length xs > 15) (map qtz [1..100]))
Run Code Online (Sandbox Code Playgroud)
没有,都将产生相同的答案,没有在子表达式的差异filter (>15) (map length (map qtz [1..100]))和filter (\ xs -> length xs > 15) (map qtz [1..100])但是。在前者中,您创建一个长度列表,然后过滤掉小于或等于 的元素15,所以这给了我们:
Prelude> filter (>15) (map length (map qtz [1..100]))
[17,20,18,18,21,21,16,16,24,112,19,19,19,107,27,22,22,22,35,110,30,17,17,17,105,25,25,25,113,113,20,33,20,33,20,20,108,108,28,28,28,103,23,116,23,23,23,36,36,23,111,111,31,31,18,31,18,93,18,18,106,106,119,26,26,26]
Run Code Online (Sandbox Code Playgroud)
在后者中我们制作一个序列列表,我们过滤掉小于或等于 15 个元素的序列:
Prelude> filter (\ xs -> length xs > 15) (map qtz [1..100])
[[7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1],[9,28,14,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1],[14,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1],…]Run Code Online (Sandbox Code Playgroud)
但这并不重要,因为您调用length这些列表,并且length对单个元素不感兴趣,而只对元素数量感兴趣,因此它基本上“丢弃”了列表中的元素。
使用length以确定是否列表具有比元件的给定数量更多的往往不是有效率的。如果列表包含数千个元素,它将首先确定长度,然后将其与 进行比较15。如果列表有无限长,它甚至会陷入无限循环。一些实用程序库提供了类似的功能lengthAtLeast :: Int -> [a] -> Bool,它会在达到所需长度或列表用尽的那一刻停止。
因此,您可以将该函数重写为:
import Data.List.HT(lengthAtLeast)
(length . filter (lengthAtLeast 16) . map qtz) [1..100]Run Code Online (Sandbox Code Playgroud)