【发布时间】:2011-03-09 08:35:32
【问题描述】:
id function 在 Haskell 中有哪些用途?
【问题讨论】:
-
据我所知,最有可能写出糟糕的代码:)
标签: function haskell functional-programming
id function 在 Haskell 中有哪些用途?
【问题讨论】:
标签: function haskell functional-programming
作为higher order functions(将函数作为参数的函数)的参数很有用,您希望某些特定值保持不变。
示例 1:如果某个值在 Just 中,则不考虑该值,否则返回默认值 7。
Prelude Data.Maybe> :t maybe
maybe :: b -> (a -> b) -> Maybe a -> b
Prelude Data.Maybe> maybe 7 id (Just 2)
2
示例 2:通过折叠构建函数:
Prelude Data.Maybe> :t foldr (.) id [(+2), (*7)]
:: (Num a) => a -> a
Prelude Data.Maybe> let f = foldr (.) id [(+2), (*7)]
Prelude Data.Maybe> f 7
51
我们使用id 作为基本情况,通过将函数列表与(.) 折叠在一起构建了一个新函数f。
示例 3:作为幺半群的函数的基本情况(简化)。
instance Monoid (a -> a) where
mempty = id
f `mappend` g = (f . g)
与我们的 fold 示例类似,函数可以被视为可连接的值,id 用于空大小写,(.) 作为附加值。
示例 4:一个普通的哈希函数。
Data.HashTable> h <- new (==) id :: IO (HashTable Data.Int.Int32 Int)
Data.HashTable> insert h 7 2
Data.HashTable> Data.HashTable.lookup h 7
Just 2
哈希表需要哈希函数。但是如果你的密钥已经被散列了怎么办?然后传递 id 函数,填充为你的哈希方法,性能开销为零。
【讨论】:
foldl' (.) id 比使用foldr (.) id 会更好吗?
(+) 和其他需要绑定两个参数的外部函数)做一些可怕的错误,例如更改应用程序树以折叠某些节点和他们使用的空闲内存。
id 作为继续传递样式 (CPS) 中的基本函数。
numberOfTrues :: [Bool] -> Int; numberOfTrues = length . filter id
在函数式语言中,函数是一等值
您可以作为参数传递。
因此,id 最常见的用法之一出现在
你传递一个函数作为
参数给另一个函数来告诉它要做什么。
做什么的选择之一可能是
“别管它”——在这种情况下,你通过id
作为参数。
【讨论】:
just leave it alone 是让我点击的部分,谢谢!
换一种答案:
在通过组合链接多个函数时,我经常会这样做:
foo = id
. bar
. baz
. etc
结束
foo = bar
. baz
. etc
它使编辑更容易。人们可以用其他“零”元素做类似的事情,例如
foo = return
>>= bar
>>= baz
foos = []
++ bars
++ bazs
【讨论】:
如果您操作数字,尤其是加法和乘法,您会注意到 0 和 1 的用处。同样,如果您操作列表,空列表会非常方便。同样,如果您操作函数(在函数式编程中很常见),您会注意到 id 的相同用途。
【讨论】:
假设您正在寻找某种谜题的解决方案,在该谜题中,您在每个回合都采取行动。您从候选职位pos 开始。在每个阶段,您都可以对pos 进行可能的转换列表(例如,在拼图中滑动一块)。在函数式语言中,将转换表示为函数是很自然的,因此现在您可以使用函数列表制作移动列表。如果“什么都不做”在这个谜题中是合法的,那么你可以用id 来表示它。如果您不这样做,那么您需要将“什么都不做”作为与“做某事”不同的特殊情况来处理。通过使用id,您可以在一个列表中统一处理所有案例。
这可能是几乎所有id 的用法都存在的原因。将“无为”与“做某事”统一处理。
【讨论】:
每当您需要在某个地方拥有一个函数,但想做的不仅仅是保持它的位置(以“未定义”为例)。
正如上面提到的(即将成为)斯图尔特博士所说,当您需要将函数作为参数传递给另一个函数时,它也很有用:
join = (>>= id)
或作为函数的结果:
let f = id in f 10
(大概,你稍后会编辑上面的函数来做一些更“有趣”的事情......;)
正如其他人所提到的,id 是一个很好的占位符,当您需要某个功能时。
【讨论】:
我还可以帮助您提高高尔夫成绩。而不是使用
($)
您可以使用 id 保存单个字符。
例如
zipWith id [(+1), succ] [2,3,4]
一个有趣的,不仅仅是有用的结果。
【讨论】:
zipWith($)[][] 与 zipWith id[][].
因为我们找到了id 的不错应用。在这里,有一个回文:)
import Control.Applicative
pal :: [a] -> [a]
pal = (++) <$> id <*> reverse
【讨论】:
<*> reverse 建议 instance Applicative ((->) r) 其中 (f <*> g) x = f x (g x) 和 ($) = (.) 所以 pal x = ((++).id) x (reverse x) = x ++ reverse x。 (这表明pal = (++) <*> reverse 也有效)。
假设您是一台计算机,即您可以执行一系列步骤。那么如果我想让你保持现在的状态,但我总是要给你一个指令(我不能只是沉默,让时间过去),我给你什么指令? Id 是为此创建的函数,用于返回不变的参数(在以前的计算机的情况下,参数将是它的状态)并为其命名。只有当你有高阶函数时,这种必要性才会出现,当你使用函数而不考虑它们内部的内容时,这迫使你象征性地表示甚至“什么都不做”的实现。类似地,0 被视为某种事物的数量,是没有数量的象征。实际上在代数中 0 和 id 分别被认为是操作 + 和 ∘(函数组合)的中性元素,或者更正式地说:
对于所有类型编号的 x:
对于所有 f 类型的函数:
【讨论】: