Haskell Prelude中'const'的重点是什么?

时间:2011-09-13 13:17:52

标签: haskell

通过Haskell Prelude,我see a function const

const x _ = x

我似乎无法找到与此功能相关的任何内容。

重点是什么?任何人都可以举例说明可以使用这个函数吗?

9 个答案:

答案 0 :(得分:79)

当您不需要所有灵活性时,它可用于传递到高阶函数。例如,monadic序列运算符>>可以根据monadic绑定运算符定义为

x >> y = x >>= const y

它比使用lambda

更整洁
x >> y = x >>= \_ -> y

你甚至可以免费使用它

(>>) = (. const) . (>>=)

虽然在这种情况下我并不特别推荐。

答案 1 :(得分:26)

添加到hammar的优秀直接答案:像constid这样的简单函数作为高阶函数非常有用,原因与它们是基础相同SKI combinator calculus

并不是说我认为haskell的前奏函数是在正式系统之后有意识地建模的。只是在haskell中创建丰富的抽象非常容易,所以你经常会看到这些类型的理论事物在实际上很有用。

无耻插件,但我在博客中谈到(->)的Applicative实例实际上是SK组合器here的情况,如果那是你的那种话成。

答案 2 :(得分:22)

使用const的一个简单示例是Data.Functor.(<$)。有了这个功能,你可以说:我这里有一个带有无聊内容的仿函数,但我希望在其中有其他有趣的东西,而不改变仿函数的形状。 E.g。

import Data.Functor

42 <$ Just "boring"
--> Just 42

42 <$ Nothing
--> Nothing

"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]

定义是:

(<$) :: a -> f b -> f a
(<$) =  fmap . const

或写得不是毫无意义:

cool <$ uncool =  fmap (const cool) uncool

您会看到const如何用于“忘记”输入。

答案 3 :(得分:16)

  

我似乎无法找到与此功能相关的任何内容。

许多其他答案讨论const相对深奥(至少对新人)的应用。这是一个简单的问题:你可以使用const去掉一个带有两个参数的lambda,扔掉第一个参数,但用第二个参数做一些有趣的事情。

例如,length

的以下(低效!)实现
length' = foldr (\_ acc -> 1 + acc) 0

可以改写为

length' = foldr (const (1+)) 0

这可能更优雅。

表达式const (1+)确实在语义上等同于\_ acc -> 1 + acc,因为它接受一个参数,将其抛弃,并返回(1+)部分。

答案 4 :(得分:14)

另一个用途是实现具有伪参数的类成员函数,该伪参数不应被评估(用于解析模糊类型)。可以在Data.bits中的示例:

instance Bits Int where
  isSigned = const True
  bitSize  = const wordSize
  ...

通过使用const,我们明确地说我们正在定义常量值。

我个人不喜欢使用伪参数,但如果在类中使用它们,那么这是编写实例的一种相当不错的方式。

答案 5 :(得分:2)

const可能只是您正在寻找的与其他功能一起使用的实现。这是我发现的一个例子。

假设我们想要将2元组的结构重写为另一个2元组结构。我可以这样说:

((a,b),(c,d)) ⇒ (a,(c,(5,a)))

我可以用模式匹配给出一个直接的定义:

f ((a,b),(c,d)) = (a,(c,(5,a)))

如果我想要这种重写的无意义(默认)解决方案怎么办?有些思考和摆弄以后,答案是我们可以用(&&&), const, (.), fst, snd表达任何重写。请注意,(&&&)来自Control.Arrow

使用这些函数的示例的解决方案是:

(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))

请注意与(a,(c,(5,a)))的相似性。如果我们将&&&替换为,怎么办?然后它写着:

(fst.fst, (fst.snd, (const 5, fst.fst)))

注意a是第一个元素的第一个元素,那就是fst.fst个项目。注意c如何是第二个元素的第一个元素,那就是fst.snd个项目。也就是说,变量成为其源头的途径。

const允许我们引入常量。有趣的是这个名字如何与意义排列在一起!

然后我用Applicative概括了这个想法,这样你就可以用无意义的风格编写任何函数(只要你有案例分析作为函数提供,例如maybeeither,{{1 }})。同样,bool扮演引入常量的角色。您可以在Data.Function.Tacit包中看到这项工作。

当你从抽象地开始,在目标上,然后努力实现时,你可能会对答案感到惊讶。也就是说,任何一个功能都可能与机器中的任何一个齿轮一样神秘。但是,如果您撤回以使整个机器进入视图,您可以理解该cog所必需的上下文。

答案 6 :(得分:2)

假设您要创建一个Nothings列表,该列表等于字符串的长度。当const返回其第一个参数时,无论第二个参数如何,您都可以这样做:

listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing

或更明确地说:

listOfNothing st = map (const Nothing) st

答案 7 :(得分:0)

说您要旋转列表。在Haskell中,这是一种惯用的方法:

rotate :: Int -> [a] -> [a] rotate _ [] = [] rotate n xs = zipWith const (drop n (cycle xs)) xs

此函数使用函数const压缩两个数组,第一个是无限循环数组,第二个是您开始使用的数组。

const充当边界检查,并使用原始数组终止循环数组。

请参阅:Rotate a list in Haskell

答案 8 :(得分:0)

  

我似乎找不到与此功能相关的任何内容。

假设您要生成给定列表的所有子序列。

对于每个列表元素,在给定的点上,您可以选择True(在当前子序列中包括它)或False(不包括它)。可以使用filterM函数来完成。

赞:

 λ> import Control.Monad
 λ> :t filterM
 filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
 λ> 

例如,我们需要[1..4]的所有子序列。

 λ> filterM  (const [True, False])  [1..4]
 [[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
 λ>