通过Haskell Prelude,我see a function const
:
const x _ = x
我似乎无法找到与此功能相关的任何内容。
重点是什么?任何人都可以举例说明可以使用这个函数吗?
答案 0 :(得分:79)
当您不需要所有灵活性时,它可用于传递到高阶函数。例如,monadic序列运算符>>
可以根据monadic绑定运算符定义为
x >> y = x >>= const y
它比使用lambda
更整洁x >> y = x >>= \_ -> y
你甚至可以免费使用它
(>>) = (. const) . (>>=)
虽然在这种情况下我并不特别推荐。
答案 1 :(得分:26)
添加到hammar的优秀直接答案:像const
和id
这样的简单函数作为高阶函数非常有用,原因与它们是基础相同SKI combinator calculus。
并不是说我认为haskell的前奏函数是在正式系统之后有意识地建模的。只是在haskell中创建丰富的抽象非常容易,所以你经常会看到这些类型的理论事物在实际上很有用。
无耻插件,但我在博客中谈到(->)
的Applicative实例实际上是S
和K
组合器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概括了这个想法,这样你就可以用无意义的风格编写任何函数(只要你有案例分析作为函数提供,例如maybe
,either
,{{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
充当边界检查,并使用原始数组终止循环数组。
答案 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],[]]
λ>