浏览Haskell前奏,我看到一个函数 const
:
const x _ = x
我似乎找不到与此功能相关的任何内容。
重点是什么?谁能举例说明该功能的使用位置?
Answers:
当您不需要所有灵活性时,将其传递给高阶函数很有用。例如,>>
可以根据单子绑定算子将单子序列算符定义为
x >> y = x >>= const y
比使用lambda更加整洁
x >> y = x >>= \_ -> y
你甚至可以免费使用它
(>>) = (. const) . (>>=)
尽管在这种情况下,我不建议这样做。
map (const 42) [1..5]
结果[42, 42, 42, 42, 42]
。
const
在将一个参数应用于需要一个函数(例如传递给map
)的函数时很有用。
head = foldr const (error "Prelude.head: empty list")
((->) e)
也是阅读器monad- Reader
以及类似的东西只是newtype
包装器-然后ask
函数是id
,所以这也是I
组合器。如果你看一下而不是在哈斯克尔库里的原BCKW基础B
,K
以及W
为fmap
,return
,和join
分别。
一个简单的使用示例const
是Data.Functor.(<$)
。使用此功能,您可以说:我的函子里面有些无聊,但我想在其中包含其他有趣的东西,而无需更改函子的形状。例如
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
将在此处看到如何“忘记”输入内容。
我似乎找不到与此功能相关的任何内容。
许多其他答案都讨论了的相对深奥的应用(至少对于新手而言)const
。这是一个简单的例子:您可以const
用来摆脱带有两个参数的lambda,抛弃第一个参数,但对第二个参数做一些有趣的事情。
例如,以下(效率低下但有启发性的)实现 length
,
length' = foldr (\_ acc -> 1 + acc) 0
可以改写成
length' = foldr (const (1+)) 0
这也许更优雅。
该表达式const (1+)
确实在语义上等效于\_ acc -> 1 + acc
,因为它接受一个参数,将其扔掉,然后返回section (1+)
。
另一个用途是实现具有伪参数的类成员函数,该伪参数不应进行评估(用于解析歧义类型)。可能在Data.bits中的示例:
instance Bits Int where
isSigned = const True
bitSize = const wordSize
...
通过使用const,我们明确表示正在定义常量值。
我个人不喜欢使用伪参数,但是如果在类中使用伪参数,那么这是编写实例的一种不错的方法。
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
允许我们引入常量。有趣的是名称与含义如何对齐!
然后我概括这个想法与应用型,让您可以在一个毫无意义的风格编写的任何函数(只要您有可用的功能,如案例分析maybe
,either
,bool
)。再次,const
起到引入常量的作用。您可以在Data.Function.Tacit中看到此工作包中。
当您抽象地开始目标时,然后为实现而努力时,答案可能会让您感到惊讶。也就是说,任何一种功能都可能像机器中任何一个齿轮一样神秘。但是,如果撤回查看整个机器,则可以了解需要该齿轮的环境。
假设您要旋转列表。在Haskell中,这是一种惯用的方法:
rotate :: Int -> [a] -> [a]
rotate _ [] = []
rotate n xs = zipWith const (drop n (cycle xs)) xs
此函数使用函数压缩两个数组const
,第一个是无限循环数组,第二个是您开始使用的数组。
const
充当边界检查,并使用原始数组终止循环数组。
看到: 在Haskell中旋转列表
我似乎找不到与此功能相关的任何内容。
假设您要生成给定列表的所有子序列。
对于每个列表元素,在给定的点上,您可以选择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],[]]
λ>
backgroundColor :: Text -> Color
对我来说backgroundColor = const White