首页 文章

打破haskell功能

提问于
浏览
2

我正在阅读真实世界的哈斯克尔书,它更有意义 . 我已经完成了这个功能,并想知道我对它正在做什么的解释是否正确 . 功能是

oddList :: [Int] -> [Int]

oddList (x:xs) | odd x     = x : oddList xs
               | otherwise = oddList xs
oddList _                  = []

我读过那个

定义函数oddList,它接受一个int列表并返回一个int列表 .

模式匹配:当参数是列表时 .

取第一项,将其绑定到x,将余数元素保留为xs .

如果x是奇数,则将x应用于将oddList应用于剩余元素xs并返回该结果的结果 . 重复...

当x不是奇数时,只返回将oddList应用于xs的结果

在所有其他情况下,返回一个空列表 .

1)这是一种合适/正确的阅读方式吗?

2)即使我认为我理解它,我也不相信我已经将(x:xs)位缩小了 . 应该如何阅读,它实际上在做什么?

3)是| ... |否则语法与语法的case expr相似/相同

4 回答

  • 2

    你做对了 .

    (x:xs) 部分说:如果列表包含至少一个元素,则将第一个元素绑定到 x ,将列表的其余部分绑定到 xs

    代码也可以写成

    oddList :: [Int] -> [Int]
    oddList (x:xs) = case (odd x) of
                       True  -> x : oddList xs
                       False -> oddList xs
    oddList _ = []
    

    在这个特定的情况下,守卫( | )只是一种更好的方式来写下来 . 请注意, otherwise 只是 True 的同义词,这通常使代码更易于阅读 .


    @DanielWagner指出的是,在某些情况下,我们使用警卫可以实现更复杂的行为 .

    考虑这个功能(仅用于说明原理)

    funnyList :: [Int] -> [Int]
    funnyList (x1:x2:xs)
        | even x1 && even x2 = x1 : funnyList xs
        | odd x1 && odd x2   = x2 : funnyList xs
    funnyList (x:xs)
        | odd x     = x : funnyList xs
    funnyList _ = []
    

    这个函数将通过这些子句,直到其中一个为真:

    • 如果至少有两个元素( x1x2and 它们都是偶数,那么结果是:

    • 将第一个元素( x1 )添加到处理列表其余部分的结果中(不包括 x1x2

    • 如果列表中至少有一个元素( x ),则 and 为奇数,则结果为:

    • 将第一个元素( x )添加到处理列表其余部分的结果中(不包括 x

    • 无论列表如何,结果如下:

    • 一个空列表 []

    因此 funnyList [1,3,4,5] == [1,3]funnyList [1,2,4,5,6] == [1,2,5]


    您还应该查看免费在线图书Learn You a Haskell for Great Good

  • 3

    1我只对您的描述进行了2次更改:

    • 当参数是非空列表时 .

    • f x是将奇数列表应用于剩余元素xs并返回该结果的奇数前缀x . [删除“重复...”“]

    请注意,对于“_”,“在所有其他情况下”实际上意味着“当参数是空列表时”,因为这是唯一的其他情况 .

    2 (x:xs) 是一个引入两个变量的模式 . 该模式匹配非空列表,并将 x 变量绑定到列表的第一个项目(头部),并将 xs 绑定到列表的余数(尾部) .

    3是的 . 编写相同函数的等效方法是

    oddList :: [Int] - > [Int]

    oddList ys = case ys of { (x:xs) | odd x     -> x : oddList xs ;
                               (x:xs) | otherwise -> oddList xs ;
                               _                  -> [] }
    

    注意 otherwiseTrue 相同,因此这里可以省略 | otherwise .

  • 2

    你已经正确地理解它在低级别上做了什么 .

    但是,根据一些经验,您应该能够立即在"big picture"中解释它:当您有两个案例 (x:xs)_ ,并且 xs 再次仅作为函数的参数再次出现时,这意味着这是一个列表使用者 . 实际上,这样的函数总是等于 foldr . 你的功能有表格

    oddList' (x:xs) = g x $ oddList' xs
    oddList' [] = q
    

    g :: Int -> [Int] -> [Int]
    g x qs | odd x      = x : qs
           | otherwise  = qs
    q = [] :: [Int]
    

    因此可以将该定义压缩为 oddList' = foldr g q .

    虽然你可能现在对折叠不比使用显式递归更舒服,但实际上,一旦你看过它几次就会更简单 .

    实际上,这个例子当然可以做得更简单: oddList'' = filter odd .

  • 1

    (x:xs) 读为:使用 (x:xs) 形式的表达式构造的列表

    然后,确保您了解每个非空列表 must 都是使用(:)构造函数构造的 .

    当您考虑列表类型只有2个构造函数时,这是显而易见的:[]构造空列表,而(a:xs)构造头部为a且尾部为xs的列表 .

    你还需要在心理上去除像糖这样的表达

    [a,b,c] = a : b : c : []
    

    "foo" = 'f' : 'o' : 'o' : []
    

    这种语法糖是列表和其他类型如Maybe,Either之间的唯一区别或者你自己的类型 . 例如,当你写作

    foo (Just x) = ....
    foo Nothing  = .....
    

    我们也在考虑Maybe的两个基本案例:

    • 它是用 Just 构建的

    • 它是用 Nothing 构建的

相关问题