例如,Haskell
具有id
功能,Julia具有identity
功能,并且SO上的许多问题都涉及身份功能。 (我想在Python中你可以做lambda x:x
)
我一直在试图想到这个功能的用例;我失败了。
身份功能的目的是什么,它的常见用例是什么?
答案 0 :(得分:18)
请记住,在Haskell函数中是第一类值,可以像其他值一样用作数据,并作为参数传递给其他函数。通常,您可以通过将其他函数相互应用来构建您真正想要使用的函数。有时你会发现你想要在一个地方使用的功能恰好比id
更复杂。
例如,这是一个否定列表中每个第二个元素的函数:
negateEverySecond = zipWith id (cycle [id, negate])
答案 1 :(得分:11)
在Julia中(在标准库中):快速grep显示当前identity
函数最突出的用途是作为各种排序相关函数的by
参数的默认值,例如作为sort!
,sort
,issorted
等
by
参数允许指定从每个对象中提取排序键的函数,以便根据a
比较两个对象b
和by(a) < by(b)
而不是a < b
。 identity
函数是默认的,因为identity(a) < identity(b)
与a < b
相同。
当by
为identity
时,还有一些内部排序代码是特殊的,这应该允许更常见的案例代码。
答案 2 :(得分:7)
id
可以是一个很好的起点。例如,
foldl f init xs = foldr (\x r -> \acc -> r (f acc x)) id xs $ init
chi提及
type K a = (a -> Int) -> Int
-- factorial, CPS
factCPS :: Int -> K Int
factCPS 0 k = k 1
factCPS n k = factCPS (n-1) (k . (*n))
-- factorial, plain
fact :: Int -> Int
fact n = factCPS n id
这种方法实际上与我上面给出的例子密切相关; id
定义中的foldl
实际上只是继续使用。
fact q
= foldl (*) 1 [1..q]
= foldr (\x r -> \acc -> r (acc * x)) id (build
(\c n -> if q<1
then n
else let go k | k <= q = k `c` go (k+1)
| otherwise = n
in go 1)) 1
-- foldr/build
= (if q < 1
then id
else let go k | k <= q = \acc -> go (k+1) (acc * k)
| otherwise = id = \acc -> acc
in go 1) 1
= (if q < 1
then id
else let go k acc | k <= q = go (k+1) (acc * k)
| otherwise = acc
in go 1) 1
= if q < 1
then 1
else let go k acc | k <= q = go (k+1) (acc*k)
| otherwise = acc
in go 1 1
答案 3 :(得分:6)
通常,您可以使用它来返回函数中未更改的参数的确切值。
例如,在maybe
。
我看到的另一个例子是(id &&& id)
- 即。将元素复制到元组中。
λ> let l = [5,3,4,1]
λ> map (id &&& id) l
[(5,5),(3,3),(4,4),(1,1)]
答案 4 :(得分:4)
撰写功能列表:
compose :: [a -> a] -> a -> a
compose = foldr (.) id
-- to be compared with
sum :: Num a => [a] -> a
sum = foldr (+) 0
有条件地追加消息:(不是最好的解决方案,也不是太糟糕)
-- string=""
-- if foo: string += "foo"
-- if bar: string += "bar"
-- print string
putStrLn .
(if bar then (++"bar") else id) .
(if foo then (++"foo") else id) $
""
延续传递风格,基本情况:
type K a = (a -> Int) -> Int
-- factorial, CPS
factCPS :: Int -> K Int
factCPS 0 k = k 1
factCPS n k = factCPS (n-1) (k . (*n))
-- factorial, plain
fact :: Int -> Int
fact n = factCPS n id
答案 5 :(得分:3)
一个相当常见的任务是从Maybe
中获取一个值。我们想写
fromMaybe :: a -> Maybe a -> a
虽然它是标准的内置功能。另一个标准内置函数是maybe
maybe :: b -> (a -> b) -> Maybe a -> b
maybe nothing just m = case m of
Nothing -> nothing
Just a -> just a
可以看作是破坏Maybe
的通用方法 - 它只是为模式匹配的每个分支传递一个函数。
最后,应该很容易看出fromMaybe
和maybe
是相关的:
fromMaybe default m = maybe default id m
或更多无点风格
fromMaybe = flip maybe id
答案 6 :(得分:3)
我经常尝试在设计中使用类似身份的函数作为转换函数的默认参数,以保持通用性和模块性尽可能高。
今天你可能想要一个使用原始数据操作的处理管道,因此你不需要对输入进行任何初步转换(pre_mapping = lambda x : x
)。在伪python代码中
def do_something(data,pre_mapping = lambda x : x)
data = pre_mapping(data)
real_foo(data)
也许明天你会意识到最好用例如数据的平方,所以,在一行中,你调用
do_something(data
, pre_mapping = lambda x : x**2)
这变得非常有用,例如如果您需要尝试其中许多pre_mapping
s。
答案 7 :(得分:3)
基本上,只要高阶函数将一个或多个函数作为参数,但实际上不需要进行任何工作,您可以传入id
。
我认为有人已经提到了maybe
函数:maybe 0 id
会将Nothing
替换为0
,但如果它是Just
那么我们实际上并不想要要更改数据,只需返回即可。所以我们传入id
。
类似的函数是const
:例如,const 7
是一个接受一个参数的函数,完全忽略它,然后返回7.为什么你会想要那个?好吧,请考虑length
的实现:
length = sum . map (const 1)
也就是说,用1替换每个元素,然后将它们全部加起来。