将Haskell代码片段组合起来,得到更完整的图像。

11

这是我在某处找到的代码,但我想知道它是如何工作的:

    findIndices :: (a -> Bool) -> [a] -> [Int]
    findIndices _ [] = []
    findIndices pred xs = map fst (filter (pred . snd) (zip [0..] xs))
输出: findIndices (== 0) [1,2,0,3,0] == [2,4], 其中pred(==0)&xs[1,2,0,3,0]

我将展示我的理解:

    (zip [0..] xs)

以上代码的作用是为列表中的每个元素添加索引。对于给定的输入,它看起来像这样:[(0,1),(1,2),(2,0),(3,3),(4,0)]

返回结果为:

以上代码的作用是为列表中的每个元素添加索引。对于给定的输入,它看起来像这样:[(0,1),(1,2),(2,0),(3,3),(4,0)]

    (pred . snd)

我发现这意味着类似于 pred (snd (x))。我的问题是,x 是否是由 zip 行生成的列表?我倾向于是,但我的猜测不确定。

接下来,关于 fstsnd 的理解。我知道

    fst(1,2) = 1 
    snd(1,2) = 2
这两个命令在代码中有什么意义? 我对filter的理解是它返回与条件匹配的项目列表。例如,
    listBiggerThen5 = filter (>5) [1,2,3,4,5,6,7,8,9,10]

将会给出[6,7,8,9,10]

我对map的理解是它将函数应用于列表中的每个项。例如,

    times4 :: Int -> Int
    times4 x = x * 4
    listTimes4 = map times4 [1,2,3,4,5]

将给出[4, 8, 12, 16, 20]

总体上这是如何工作的?我认为目前我所知道的已经非常全面了,但是还不能完全将它们串联起来。有人能帮帮我吗?


8
我很高兴地告诉您,阅读这个问题是一种难得的愉悦。我们经常收到“这段代码怎么运行?”这样的问题,但很少有人像提问者一样详细说明自己已经理解和未理解的内容。这使得编写一个有针对性的、关于您存在的漏洞的答案非常有趣。 - Daniel Wagner
谢谢你,Daniel!我在这个问题上花了很多时间,所以我能够准确定位我需要帮助的地方。 - user13231123
1
我想补充一下,@WillNess的答案也是可行的。它更加易于阅读和理解。 - user13231123
2个回答

7
我发现这意味着类似于pred (snd(x))。我的问题是,x是由zip函数生成的列表吗?我倾向于是,但只是猜测而已。
好的,pred . snd的意思是\x -> pred (snd x)。因此,它基本上构造了一个将元素x映射到pred (snd x)的函数。
因此,这意味着表达式看起来像:
filter (<b>\x -> pred (snd x)</b>) (zip [0..] xs)

这里,x是通过zip生成的一个二元组。因此要确定在结果中是否保留了(0, 1)(1,2)(2, 0)等,snd x将从这些二元组的第二个元素(即120等)中取出,并检查该元素上的pred是否被满足。如果满足,则保留该元素,否则过滤掉该元素(即过滤掉该二元组)。

因此,如果(== 0)predicate,那么filter (pred . snd) (zip [0..] xs)将包含二元组[(2, 0), (4, 0)]

但现在的结果是一个二元组的列表。如果我们想要索引,就需要摆脱这个二元组以及这些二元组的第二个元素。为此,我们使用fst :: (a, b) -> a:它将一个二元组映射到其第一个元素。因此,对于列表[(2, 0), (4, 0)]map fst [(2, 0), (4, 0)]将返回[2, 4]


1
嘿,威廉,真是太棒了的解释!我现在已经完全理解了代码。谢谢您! - user13231123

1
在Haskell中,我们喜欢说,“遵循类型”。实际上,这些部分就像通过从类型到相应类型的电线连接一样连接起来:
首先,函数组合是:
   (f >>> g) x  =  (g . f) x  =        g (f x)
   (f >>> g)    =  (g . f)    =  \x -> g (f x)

"

函数组合类型推断规则 是:

"
    f        :: a -> b                   --      x  :: a
          g  ::      b -> c              --    f x  :: b
   -------------------------             -- g (f x) :: c
    f >>> g  :: a ->      c
    g  .  f  :: a ->      c

现在,)
findIndices :: (b -> Bool) -> [b] -> [Int]
findIndices pred  = \xs -> map fst ( filter (pred . snd) ( zip [0..] xs ))
                  =        map fst . filter (pred . snd) . zip [0..]
                  =  zip [0..]  >>>  filter (snd >>> pred)  >>>  map fst
---------------------------------------------------------------------------
zip :: [a] ->          [b]        ->        [(a,  b)]
zip  [0..] ::          <b>[b]</b>        ->        <b>[(Int,b)]</b>
---------------------------------------------------------------------------
        snd           :: (a,b) -> b
                pred  ::          b -> Bool
       ------------------------------------
       (snd >>> pred) :: (a,b)      -> Bool
---------------------------------------------------------------------------
filter ::               (t          -> Bool) -> [t]   -> [t]
filter (snd >>> pred) ::                      [(a,b)] -> [(a,b)]
filter (snd >>> pred) ::                    <b>[(Int,b)]</b> -> <b>[(Int,b)]</b>
---------------------------------------------------------------------------
    fst ::                                   (a,   b) -> a
map     ::                                  (t        -> s) -> [t] -> [s]
map fst ::                                                 [(a,b)] -> [a]
map fst ::                                               <b>[(Int,b)]</b> -> <b>[Int]</b>

所以,总体来说,
zip  [0..] ::          <b>[b]</b>        ->        [(Int,b)]
filter (snd >>> pred) ::                    [(Int,b)] -> [(Int,b)]
map fst ::                                               [(Int,b)] -> <b>[Int]</b>
---------------------------------------------------------------------------
findIndices pred ::    <b>[b]</b> ->                                         <b>[Int]</b>

你问过,这些部分如何拼合在一起?

就是这样。


使用列表推导式,您的函数写成如下形式:
findIndices pred xs = [ i | (i,x) <- zip [0..] xs, pred x ]

伪代码如下:

"对于zip [0..] xs中的每个(i,x),如果pred x成立,则将i添加到结果列表中"

它通过将长度为n的列表xs转换为一个由索引和元素组成的元组列表来实现。

xs = [a,b,...,z] = [a] ++ [b] ++ ... ++ [z]

into

  [0 | pred a] ++ [1 | pred b] ++ ... ++ [n-1 | pred z]

其中 [a | True] 等同于 [a],而 [a | False] 等同于 []


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接