Следующее было размещено в комментарии, теперь удалено:
countNE = sum<$>(1<$)<<<(>>=(1`take`))
Это, безусловно, будет выглядеть пугающим для непосвященных, но на самом деле, это эквивалентно
= sum <$> (1 <$) <<< (>>= (1 `take`))
= sum <$> (1 <$) . (take 1 =<<)
= sum . fmap (const 1) . concatMap (take 1)
= sum . map (const 1) . concat . map (take 1)
что далее эквивалентно
countNE xs = sum . map (const 1) . concat $ map (take 1) xs
= sum . map (const 1) $ concat [take 1 x | x <- xs]
= sum . map (const 1) $ [ r | x <- xs, r <- take 1 x]
= sum $ [const 1 r | (y:t) <- xs, r <- take 1 (y:t)] -- sneakiness!
= sum [const 1 r | (y:_) <- xs, r <- [y]]
= sum [const 1 y | (y:_) <- xs]
= sum [ 1 | (_:_) <- xs] -- replace each
-- non-empty list
-- in
-- xs
-- with 1, and
-- sum all the 1s up!
= (length . (take 1 =<<)) xs
= (length . filter (not . null)) xs
, что должно быть намного яснее, даже если немного хитрым способом. Да, он сам по себе не рекурсивен, но и sum
, и список-понимание будут рекурсивно реализованы данной реализацией на Haskell.
Это переопределяет length
как sum . (1 <$)
и filter p xs
как [x | x <- xs, p x]
, и использует эквивалент not (null xs) === (length xs) >= 1
.
Видите? Хаскель это весело. Даже если это еще не так, но так и будет. :)