【问题标题】:Haskell function for list length列表长度的 Haskell 函数
【发布时间】:2016-01-02 09:25:38
【问题描述】:

要使用折叠器计算列表的长度,可以执行以下操作:

foldr (\_ acc -> acc + 1) 0

进一步扩展折叠函数需要增加第二个参数的想法,我想出了这个(这是错误的):

foldr ((+1) . (flip const)) 0`

对类型的进一步检查揭示了这一点:

(+1) . (flip const) :: Num (c -> c) => a -> c -> c

Haskell higher order function to calculate length 该页面上有一条有趣的评论,我真的无法理解

foldr (((+1).).(flip const)) 0

有人能解释一下这个组合是如何工作的吗?

【问题讨论】:

  • 这看起来像是用一些工具生成的,比如pointfree....(+1) . (flip const)基本上意味着为这个函数的每个应用程序添加一个,不管第一个参数是什么(这就是翻转const 在那里)。就像(\a b -> (b+1))

标签: haskell fold


【解决方案1】:

首先,让我们关注为什么foldr ((+1) . (flip const)) 0 是错误的。您只想增加第二个参数而忘记第一个参数。从语义上讲,就是

\_ a -> a + 1

但是,您写了以下内容:

(+1) . flip const
= (+1) . (\_ a -> a)
= \x -> (+1) . (\_ a -> a) $ x
= \x -> (+1) $ (\_ a -> a) $ x
= \x -> (+1) $ \a -> a
= \_ -> (+1) (\a -> a)
= const ( (+1) (\a -> a))

这就是为什么你突然需要Num (c -> c),因为你试图在id 上应用(+1)

但实际上你的意思是:

\_ a -> a + 1
= \_ a -> (+1) a
= \_   -> (+1)
= const (+1)

毕竟,您想忘记第一个参数并在第二个参数上使用函数f。您所要做的就是使用const f

组合((+1).).(flip const)过于冗长,可能由pointfree生成:

((+1).).(flip const)
= ((\x -> x + 1).) . (\a _ -> a)
= \c -> ((\x -> x + 1).) . (\a _ -> a) $ c
= \c -> ((\x -> x + 1).) $ \_ -> c
= \c -> \f -> (\x -> x + 1) . f $ \_ -> c
= \c ->   (\x -> x + 1) . \_ -> c
= \_ c -> (\x -> x + 1) $ c
= \_ c -> c + 1

【讨论】:

    【解决方案2】:

    这确实是一个评论,但对一个人来说太长了。

    除非你正在处理像懒惰的Nats 这样的奇怪数字,否则你真的想要

    length xs = foldl' (\acc _ -> 1 + acc) 0 xs
    

    让这毫无意义,

    length = foldl' (\acc -> const ((1+) acc)) 0
    length = foldl' (const . (1+)) 0
    

    如果您愿意,可以将原来的foldl' 表达式转换为foldr 形式,如下所示:

    length xs = foldr go id xs 0 where
      go _ r acc = r $! 1 + acc
    

    咀嚼go

    go _ r acc = ($!) r $ (+) 1 acc
    go _ r = ($!) r . (+1)
    go _ r = (. (+1)) (($!) r)
    go _ = (. (+1)) . ($!)
    go = const ((. (+1)) . ($!))
    

    咀嚼length

    length = flip (foldr go id) 0
    

    把它们放在一起,

    length = flip (foldr (const ((. (+1)) . ($!))) id) 0
    

    首先,我发现这种无点形式完全不透明。

    【讨论】:

    • foldl' (flip ((+).const 1)) 0 也可以。 flip 转到 foldl's 组合函数的参数顺序颠倒,((+).f) 是折叠+映射融合;所以它是半可读的。 --- 另一个是foldl' ((. const 1).(+)) 0。 :)
    • 我真的很喜欢你的回答! foldl' (const . (1+)) 0 绝对是最好的。这是另一个增加 const 内部参数的版本:foldl (on const (+1)) 0
    猜你喜欢
    • 1970-01-01
    • 2022-10-09
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    • 2020-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多