人们使用身份功能做什么?

时间:2014-09-23 04:48:32

标签: haskell functional-programming julia

例如,Haskell具有id功能,Julia具有identity功能,并且SO上的许多问题都涉及身份功能。 (我想在Python中你可以做lambda x:x

我一直在试图想到这个功能的用例;我失败了。

身份功能的目的是什么,它的常见用例是什么?

8 个答案:

答案 0 :(得分:18)

请记住,在Haskell函数中是第一类值,可以像其他值一样用作数据,并作为参数传递给其他函数。通常,您可以通过将其他函数相互应用来构建您真正想要使用的函数。有时你会发现你想要在一个地方使用的功能恰好比id更复杂。

例如,这是一个否定列表中每个第二个元素的函数:

negateEverySecond = zipWith id (cycle [id, negate])

答案 1 :(得分:11)

在Julia中(在标准库中):快速grep显示当前identity函数最突出的用途是作为各种排序相关函数的by参数的默认值,例如作为sort!sortissorted

by参数允许指定从每个对象中提取排序键的函数,以便根据a比较两个对象bby(a) < by(b)而不是a < bidentity函数是默认的,因为identity(a) < identity(b)a < b相同。

byidentity时,还有一些内部排序代码是特殊的,这应该允许更常见的案例代码。

答案 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的通用方法 - 它只是为模式匹配的每个分支传递一个函数。

最后,应该很容易看出fromMaybemaybe是相关的:

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替换每个元素,然后将它们全部加起来。

相关问题