我在理解这段代码时遇到麻烦:
let
sieve (p:xs) = p : sieve (filter (\ x -> x `mod` p /= 0) xs)
in sieve [2 .. ]
有人可以为我分解吗?我知道其中存在递归,但这就是我无法理解此示例中的递归如何工作的问题。
实际上非常优雅。
首先,我们定义一个函数sieve
,它接受元素列表:
sieve (p:xs) =
在sieve
的主体中,我们采用列表的开头(因为我们要传递无限列表[2..]
,并且2被定义为素数),并将其(懒惰!)附加到应用结果上sieve
到列表的其余部分:
p : sieve (filter (\ x -> x 'mod' p /= 0) xs)
因此,让我们看一下在其余列表上起作用的代码:
sieve (filter (\ x -> x 'mod' p /= 0) xs)
我们正在将sieve
应用于过滤列表。让我们分解一下过滤器部分的功能:
filter (\ x -> x 'mod' p /= 0) xs
filter
接受一个函数和一个应用该函数的列表,并保留满足该函数给定条件的元素。在这种情况下,filter
具有匿名功能:
\ x -> x 'mod' p /= 0
此匿名函数采用一个参数x
。它检查x
的modulus与p
(每次调用sieve
时,列表的开头):
x 'mod' p
如果模量不等于0:
x 'mod' p /= 0
然后元素x
保留在列表中。如果等于0,则将其过滤掉。这是有道理的:如果x
可被p
整除,则x
不仅可被1及其本身整除,因此不是素数。
与其他人在这里所说的相反,此函数does not实现了真正的sieve of Eratosthenes。它确实以类似的方式返回质数的初始序列,因此可以将其视为Eratosthenes的筛子。
当mipadi posted回答时,我即将解释代码;我可以删除它,但是由于我花了一些时间,并且由于我们的答案并不完全相同,因此将其保留在此处。
总而言之,请注意,您发布的代码中存在一些语法错误。正确的代码是,
let sieve (p:xs) = p : sieve (filter (\x -> x `mod` p /= 0) xs) in sieve [2..]
let x in y
定义x
,并允许在y
中使用其定义。该表达式的结果是y
的结果。因此,在这种情况下,我们定义一个函数sieve
,并将将[2..]
应用于sieve
的结果返回。
现在让我们仔细看一下该表达式的let
部分:
sieve (p:xs) = p : sieve (filter (\x -> x `mod` p /= 0) xs)
sieve
,该函数将列表作为第一个参数。(p:xs)
是一个pattern,它使p
与列表的头部匹配,xs
与尾部(除了头部)匹配。p : xs
是其第一个元素为p
的列表。 xs
是包含剩余元素的列表。因此,sieve
返回它接收到的列表的第一个元素。不查看列表的其余部分:
sieve (filter (\x -> x `mod` p /= 0) xs)
sieve
被递归调用。因此,filter
表达式将返回一个列表。filter
具有过滤器功能和一个列表。它仅返回列表中过滤功能对其返回true的那些元素。在这种情况下,filter
是要过滤的列表,
xs
是过滤器功能。
(\x -> x `mod` p /= 0)
,如果不是x
的倍数,则返回true。现在定义了p
,我们将其传递给sieve
,即从2开始的所有自然数的列表。因此,
它定义了一个生成器-一个称为“筛子”的流变流器
它说:“某些列表的筛子是列表的第一个元素(我们将其称为p),其余列表的筛子经过过滤,从而仅允许被p不可除的元素通过”。然后,它通过将所有整数从2返回到无穷大(即2,然后是所有不能被2整除的整数,等等)的筛子来开始事情。
在攻击Haskell之前,我建议Sieve of Eratosthenes。>>
它说“一些列表的筛子是列表的第一个元素(我们称之为p)和列表其余部分的筛子,过滤使得只有不能被p整除的元素才能通过”。然后通过将所有整数的筛子从2返回到无穷大(这是2然后是所有整数的筛子,不能被2整除等)来开始。
在你攻击Haskell之前我推荐The Little Schemer。