合并 Haskell 代码片段以获得更大的画面

Combining fragments of Haskell Code to get the bigger picture

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

    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)].

    (pred . snd)

我发现这意味着 pred (snd (x))。我的问题是,x 是从 zip 行生成的列表吗?我倾向于是,但我的猜测是站不住脚的。

接下来是我对fstsnd的理解。我知道

    fst(1,2) = 1 

    snd(1,2) = 2

这两个命令在代码中有何意义?

我对 filter 的理解是 returns 符合条件的项目列表。例如,

    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]

这总体上如何运作?我想到目前为止我所知道的已经很全面了,但还不能完全把各个部分拼凑起来。有人可以帮我吗?

I found that this means something like pred (snd (x)). My question is, is x the list made from the zip line? I'm leaning towards yes but my guess is flimsy.

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)] 将 return [2, 4].

在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>

你问过,这些碎片是如何组合在一起的?

就是这样。


使用 list comprehensions,你的函数写成

findIndices pred xs = [ i | (i,x) <- zip [0..] xs, pred x ]

伪代码为:

"result list contains i for each (i,x) in zip [0..] xs such that pred x holds"

它通过将 n-long

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

进入

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

其中 [a | True][a][a | False][]