【发布时间】:2011-09-13 13:17:51
【问题描述】:
【问题讨论】:
-
一个例子:
backgroundColor :: Text -> Color是我的backgroundColor = const White
标签: haskell
【问题讨论】:
backgroundColor :: Text -> Color 是我的backgroundColor = const White
标签: haskell
当您不需要它们的所有灵活性时,它对于传递给高阶函数很有用。例如,一元序列运算符>> 可以根据一元绑定运算符定义为
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")
添加到 hammar 出色的直接答案:像 const 和 id 这样的不起眼的函数作为高阶函数非常有用,因为它们在SKI combinator calculus 中是基本的。
并不是说我认为 haskell 的前奏函数是有意识地模仿那个正式的系统或任何东西。只是在 haskell 中创建丰富的抽象非常容易,因此您经常会看到这些类型的理论东西在实践中出现。
无耻的插件,但我在博客上写过 (->) 的 Applicative 实例实际上是 S 和 K 组合器 here,如果你喜欢这样的话。
【讨论】:
((->) e) 也是阅读器 monad——Reader 等只是 newtype 包装器——然后ask 函数就是 id,所以这就是I 组合器也是如此。如果您查看 Haskell Curry 的原始 BCKW 基础,B、K 和 W 分别是 fmap、return 和 join。
我似乎找不到与此功能相关的任何内容。
许多其他答案讨论了const 的相对深奥(至少对新手而言)应用。这是一个简单的例子:您可以使用const 来摆脱一个接受两个参数的 lambda,丢弃第一个参数,但对第二个参数做了一些有趣的事情。
例如,length 的以下(低效但有启发性)实现,
length' = foldr (\_ acc -> 1 + acc) 0
可以改写为
length' = foldr (const (1+)) 0
这也许更优雅。
表达式const (1+) 在语义上确实等同于\_ acc -> 1 + acc,因为它接受一个参数,将其丢弃,然后返回部分 (1+)。
【讨论】:
使用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 来“忘记”输入。
【讨论】:
另一个用途是实现具有不应评估的虚拟参数的类成员函数(用于解决模棱两可的类型)。可能在 Data.bits 中的示例:
instance Bits Int where
isSigned = const True
bitSize = const wordSize
...
通过使用 const,我们明确表示我们正在定义常量值。
我个人不喜欢使用虚拟参数,但如果在类中使用它们,那么这是编写实例的一种相当不错的方式。
【讨论】:
假设您要创建一个Nothings 的列表,该列表等于字符串的长度。由于const 返回它的第一个参数,不管是第二个,你都可以这样做:
listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing
或者,更明确地说:
listOfNothing st = map (const Nothing) st
【讨论】:
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、bool)。同样,const 扮演着引入常量的角色。您可以在 Data.Function.Tacit 包中看到这项工作。
当您从抽象的目标开始,然后朝着实现的方向努力时,您可能会对答案感到惊讶。也就是说,任何一种功能都可能像机器中的任何一个齿轮一样神秘。但是,如果您将整个机器拉回来,您可以了解需要该齿轮的上下文。
【讨论】:
假设您要轮换列表。这是在 Haskell 中的惯用方式:
rotate :: Int -> [a] -> [a]
rotate _ [] = []
rotate n xs = zipWith const (drop n (cycle xs)) xs
此函数使用函数const 压缩两个数组,第一个是无限循环数组,第二个是您开始使用的数组。
const作为边界检查,使用原始数组终止循环数组。
【讨论】:
我似乎找不到与此功能相关的任何内容。
假设您想生成给定列表的所有子序列。
对于每个列表元素,在给定点,您可以选择 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],[]]
λ>
【讨论】: