它们之间有什么区别吗?

xve*_*erq 7 haskell

我刚开始学习 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)

Wil*_*sem 6

没有,都将产生相同的答案,没有在子表达式的差异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)